From b8592d63b7e8b5ace8cedea2ffdd695f41697229 Mon Sep 17 00:00:00 2001 From: GitLqr Date: Sun, 4 Aug 2024 05:29:55 +0000 Subject: [PATCH] Auto deploy from Github Actions --- 404.html | 33 + about/GitLqr/index.html | 142 ++++ about/LinXunFeng/index.html | 183 +++++ ads.txt | 1 + app-ads.txt | 1 + archives/index.html | 238 ++++++ assets/css/0.styles.336be721.css | 1 + assets/img/search.83621669.svg | 1 + assets/js/10.91fde849.js | 1 + assets/js/100.f3dd0e2b.js | 1 + assets/js/101.21943b2d.js | 1 + assets/js/102.2bc77eeb.js | 1 + assets/js/103.5a9b8fdf.js | 1 + assets/js/104.9e5262e1.js | 1 + assets/js/105.f584d8aa.js | 1 + assets/js/106.9459ada7.js | 1 + assets/js/107.c6735fa5.js | 1 + assets/js/108.3c1de7a6.js | 1 + assets/js/109.4531e506.js | 1 + assets/js/11.2331f9b8.js | 1 + assets/js/110.1238c012.js | 1 + assets/js/111.8eded375.js | 1 + assets/js/112.0b4e5a43.js | 1 + assets/js/113.8ac5df7a.js | 1 + assets/js/114.e0f5ab9c.js | 1 + assets/js/115.f9a34f03.js | 1 + assets/js/116.e3223d76.js | 1 + assets/js/117.8634c1d7.js | 1 + assets/js/118.186377c7.js | 1 + assets/js/119.b9f44223.js | 1 + assets/js/12.e8428df9.js | 1 + assets/js/120.c0bd832d.js | 1 + assets/js/121.997c5f8d.js | 1 + assets/js/122.7f4b0ebb.js | 1 + assets/js/123.b52eea15.js | 1 + assets/js/124.e5c4591e.js | 1 + assets/js/125.aa402bfb.js | 1 + assets/js/126.e39ce489.js | 1 + assets/js/127.a6aa9d8a.js | 1 + assets/js/128.5f11baed.js | 1 + assets/js/129.1c49caac.js | 1 + assets/js/13.0f67029d.js | 1 + assets/js/130.b277ccc3.js | 1 + assets/js/131.5606c445.js | 1 + assets/js/132.cfe3f25f.js | 1 + assets/js/133.9616d190.js | 1 + assets/js/134.972bd9fb.js | 1 + assets/js/135.a29157ee.js | 1 + assets/js/136.43d150dd.js | 1 + assets/js/137.d3bfeadd.js | 1 + assets/js/138.31c4d124.js | 1 + assets/js/139.62bcfa96.js | 1 + assets/js/14.9854ca03.js | 1 + assets/js/140.add30aaf.js | 1 + assets/js/141.54f29e7a.js | 1 + assets/js/142.9f012dbb.js | 1 + assets/js/143.64c7beef.js | 1 + assets/js/144.cc019022.js | 1 + assets/js/145.caa7eeb2.js | 1 + assets/js/146.e9731e8e.js | 1 + assets/js/147.35098ba3.js | 1 + assets/js/148.b9c706c3.js | 1 + assets/js/149.73f1b672.js | 1 + assets/js/15.b489e01f.js | 1 + assets/js/150.cf00474f.js | 1 + assets/js/151.ac116fd1.js | 1 + assets/js/152.fe09d4d0.js | 1 + assets/js/153.6aea176e.js | 1 + assets/js/154.8228e5b4.js | 1 + assets/js/155.f1c0ec1c.js | 1 + assets/js/156.836f8c0b.js | 1 + assets/js/157.ee8ce36a.js | 1 + assets/js/158.2fa330aa.js | 1 + assets/js/159.384813a3.js | 1 + assets/js/16.1fd725ac.js | 1 + assets/js/160.efff9156.js | 1 + assets/js/161.6c996c2d.js | 1 + assets/js/162.0e55356e.js | 1 + assets/js/163.9288f368.js | 1 + assets/js/164.77c3a49b.js | 1 + assets/js/165.a0fd389c.js | 1 + assets/js/166.286a239f.js | 1 + assets/js/167.59756ff1.js | 1 + assets/js/168.fa83e66f.js | 1 + assets/js/169.bc6dd922.js | 1 + assets/js/17.471562b1.js | 1 + assets/js/170.8d0af8b3.js | 1 + assets/js/171.66d51e42.js | 1 + assets/js/172.21026f63.js | 1 + assets/js/173.a01dd23d.js | 1 + assets/js/174.4da76827.js | 1 + assets/js/175.9c09be49.js | 1 + assets/js/176.fd9291e1.js | 1 + assets/js/177.c3fdf3da.js | 1 + assets/js/178.b68b15a8.js | 1 + assets/js/179.b2f4fd5a.js | 1 + assets/js/18.6fe273b5.js | 1 + assets/js/180.4e654f2c.js | 1 + assets/js/181.cdf0f566.js | 1 + assets/js/182.320b5cb8.js | 1 + assets/js/183.258eee3b.js | 1 + assets/js/184.bf20e6a1.js | 1 + assets/js/185.ec7377be.js | 1 + assets/js/186.c4d4e096.js | 1 + assets/js/187.47b1a253.js | 1 + assets/js/188.aeddd400.js | 1 + assets/js/189.12ccf7cc.js | 1 + assets/js/19.40faf6f7.js | 1 + assets/js/190.866daee0.js | 1 + assets/js/191.5396824f.js | 1 + assets/js/192.2b856694.js | 1 + assets/js/193.aec16182.js | 1 + assets/js/194.024af9df.js | 1 + assets/js/195.ecf871dc.js | 1 + assets/js/196.f0b09f4f.js | 1 + assets/js/197.95e0485d.js | 1 + assets/js/198.68fe6ccc.js | 1 + assets/js/199.9f4ceccb.js | 1 + assets/js/2.fa15aff4.js | 14 + assets/js/20.9c50cd55.js | 1 + assets/js/200.c10d6697.js | 1 + assets/js/201.104c2fe8.js | 1 + assets/js/202.051fc2e2.js | 1 + assets/js/203.4ccb431e.js | 1 + assets/js/204.1e4af193.js | 1 + assets/js/205.7a5ac9ff.js | 1 + assets/js/206.cbeee199.js | 1 + assets/js/207.bf259bf2.js | 1 + assets/js/208.1e03267f.js | 1 + assets/js/209.0e585c42.js | 1 + assets/js/21.dab938ff.js | 1 + assets/js/210.ce8de1d4.js | 1 + assets/js/211.a190bfad.js | 1 + assets/js/212.411583ff.js | 1 + assets/js/213.8421540b.js | 1 + assets/js/214.15a34ffc.js | 1 + assets/js/215.8016ddfe.js | 1 + assets/js/216.dfaf6529.js | 1 + assets/js/217.56ae95ee.js | 1 + assets/js/218.2193df0b.js | 1 + assets/js/219.a721a7f1.js | 1 + assets/js/22.e3bbe947.js | 1 + assets/js/220.afd3ac7e.js | 1 + assets/js/221.d8d0d908.js | 1 + assets/js/222.adabf072.js | 1 + assets/js/223.c91ef62c.js | 1 + assets/js/224.2df4e374.js | 1 + assets/js/225.e7f77ec8.js | 1 + assets/js/226.3b853f48.js | 1 + assets/js/227.53d5dc87.js | 1 + assets/js/228.a58f8b25.js | 1 + assets/js/229.8c70f793.js | 1 + assets/js/23.fcbbd97e.js | 1 + assets/js/230.ecbcebee.js | 1 + assets/js/231.0b56153b.js | 1 + assets/js/232.b7b3d0b4.js | 1 + assets/js/233.f232d862.js | 1 + assets/js/234.04bd13a9.js | 1 + assets/js/235.bd29d721.js | 1 + assets/js/236.0691e9c4.js | 1 + assets/js/237.0ac1cc2c.js | 1 + assets/js/238.a9836170.js | 1 + assets/js/239.9a0cd159.js | 1 + assets/js/24.5ed1039b.js | 1 + assets/js/240.b95bbcb4.js | 1 + assets/js/241.89339429.js | 1 + assets/js/242.f17d88a2.js | 1 + assets/js/243.ba8f1a46.js | 1 + assets/js/25.04b0828d.js | 1 + assets/js/26.e10e2150.js | 1 + assets/js/27.d7d80ba6.js | 1 + assets/js/28.0ae7ec4a.js | 1 + assets/js/29.a3d6ab21.js | 1 + assets/js/3.5cf9d08f.js | 1 + assets/js/30.42612fcc.js | 1 + assets/js/31.563a23ab.js | 1 + assets/js/32.0cd95675.js | 1 + assets/js/33.4fe8f9da.js | 1 + assets/js/34.656dc501.js | 1 + assets/js/35.e7415926.js | 1 + assets/js/36.03ae92be.js | 1 + assets/js/37.e18c8add.js | 1 + assets/js/38.44ac0dde.js | 1 + assets/js/39.87f5b5df.js | 1 + assets/js/4.fe283263.js | 1 + assets/js/40.6bd5b71c.js | 1 + assets/js/41.b33fcd27.js | 1 + assets/js/42.ca77ab0a.js | 1 + assets/js/43.99eaf4fa.js | 1 + assets/js/44.4649fcde.js | 1 + assets/js/45.38401a83.js | 1 + assets/js/46.d2d9b1e6.js | 1 + assets/js/47.0209e99c.js | 1 + assets/js/48.47ad2024.js | 1 + assets/js/49.9e80286d.js | 1 + assets/js/5.f826da54.js | 1 + assets/js/50.0102ec23.js | 1 + assets/js/51.5b2cd17d.js | 1 + assets/js/52.168a55a9.js | 1 + assets/js/53.c33541bb.js | 1 + assets/js/54.adaef113.js | 1 + assets/js/55.7690e3e1.js | 1 + assets/js/56.d02001b6.js | 1 + assets/js/57.bebcd4b5.js | 1 + assets/js/58.4ce4bd83.js | 1 + assets/js/59.ef67227c.js | 1 + assets/js/6.c1094bfd.js | 1 + assets/js/60.859377cb.js | 1 + assets/js/61.3a1c277b.js | 1 + assets/js/62.cdfee049.js | 1 + assets/js/63.5f5de475.js | 1 + assets/js/64.ebb1fc9e.js | 1 + assets/js/65.3c560012.js | 1 + assets/js/66.fc96e675.js | 1 + assets/js/67.5eb144e1.js | 1 + assets/js/68.ef8efcb6.js | 1 + assets/js/69.ab9de634.js | 1 + assets/js/7.51b29a13.js | 1 + assets/js/70.60f8d760.js | 1 + assets/js/71.53b41eda.js | 1 + assets/js/72.c0aa0b43.js | 1 + assets/js/73.674ab9c7.js | 1 + assets/js/74.c9968e9e.js | 1 + assets/js/75.4aa6a762.js | 1 + assets/js/76.db0f1b6a.js | 1 + assets/js/77.1533e241.js | 1 + assets/js/78.3e8eaf07.js | 1 + assets/js/79.9934bc36.js | 1 + assets/js/8.10b56f02.js | 1 + assets/js/80.25941025.js | 1 + assets/js/81.6176dbdd.js | 1 + assets/js/82.efb50e5d.js | 1 + assets/js/83.562bf197.js | 1 + assets/js/84.b772a17c.js | 1 + assets/js/85.182832b9.js | 1 + assets/js/86.e5c39dda.js | 1 + assets/js/87.e22a17ac.js | 1 + assets/js/88.cf95790a.js | 1 + assets/js/89.72e8641b.js | 1 + assets/js/9.8e5c0c5b.js | 1 + assets/js/90.8d1a4842.js | 1 + assets/js/91.d103c54d.js | 1 + assets/js/92.54530957.js | 1 + assets/js/93.de2adb2d.js | 1 + assets/js/94.2c00c239.js | 1 + assets/js/95.8bc59368.js | 1 + assets/js/96.ad76297a.js | 1 + assets/js/97.c9c5f8b6.js | 1 + assets/js/98.581b98d0.js | 1 + assets/js/99.efe771cf.js | 1 + assets/js/app.87384e03.js | 16 + backend/index.html | 113 +++ categories/index.html | 454 ++++++++++++ img/favicon.ico | Bin 0 -> 16958 bytes index.html | 115 +++ js/global.js | 7 + mobile/index.html | 283 +++++++ more/index.html | 98 +++ note/designpattern/index.html | 106 +++ note/flutter/index.html | 92 +++ note/flutter/lxf/review/index.html | 87 +++ note/kotlin/index.html | 118 +++ pages/00e6eb/index.html | 110 +++ pages/022231/index.html | 141 ++++ pages/030ff5/index.html | 398 ++++++++++ pages/0422cb/index.html | 160 ++++ pages/04a87c/index.html | 281 +++++++ pages/05278d/index.html | 163 +++++ pages/0790a0/index.html | 170 +++++ pages/07f268/index.html | 289 ++++++++ pages/084bc4/index.html | 252 +++++++ pages/084bf9/index.html | 127 ++++ pages/087ea3/index.html | 558 ++++++++++++++ pages/0909a3/index.html | 142 ++++ pages/091864/index.html | 252 +++++++ pages/0c1879/index.html | 278 +++++++ pages/0d47a0/index.html | 233 ++++++ pages/0d4d47/index.html | 131 ++++ pages/0e461d/index.html | 143 ++++ pages/0ee6a4/index.html | 167 +++++ pages/0f2500/index.html | 352 +++++++++ pages/0f3155/index.html | 501 +++++++++++++ pages/0fcdc8/index.html | 245 +++++++ pages/10277d/index.html | 349 +++++++++ pages/1084c4/index.html | 106 +++ pages/117e5d/index.html | 148 ++++ pages/1195b1/index.html | 294 ++++++++ pages/13365e/index.html | 202 +++++ pages/13bf45/index.html | 270 +++++++ pages/13fb2b/index.html | 151 ++++ pages/15960b/index.html | 148 ++++ pages/15d73d/index.html | 377 ++++++++++ pages/1751fb/index.html | 192 +++++ pages/17d6bb/index.html | 139 ++++ pages/19fce1/index.html | 452 ++++++++++++ pages/1af1e6/index.html | 203 ++++++ pages/1cb0c1/index.html | 116 +++ pages/1d7592/index.html | 156 ++++ pages/2069ac/index.html | 158 ++++ pages/21e20f/index.html | 428 +++++++++++ pages/23e976/index.html | 220 ++++++ pages/242c2b/index.html | 127 ++++ pages/253dac/index.html | 203 ++++++ pages/26b199/index.html | 303 ++++++++ pages/29287b/index.html | 193 +++++ pages/294865/index.html | 277 +++++++ pages/2a27e8/index.html | 197 +++++ pages/2afdff/index.html | 137 ++++ pages/2c5ae4/index.html | 548 ++++++++++++++ pages/2ed5f7/index.html | 175 +++++ pages/2ffb7f/index.html | 116 +++ pages/326c98/index.html | 199 +++++ pages/328069/index.html | 124 ++++ pages/32a06e/index.html | 142 ++++ pages/334ef0/index.html | 132 ++++ pages/337d0f/index.html | 106 +++ pages/3420a3/index.html | 148 ++++ pages/34334d/index.html | 156 ++++ pages/34e11b/index.html | 121 +++ pages/37adf1/index.html | 131 ++++ pages/39525c/index.html | 221 ++++++ pages/398f01/index.html | 183 +++++ pages/399558/index.html | 91 +++ pages/3a12b8/index.html | 275 +++++++ pages/3b59d7/index.html | 227 ++++++ pages/41ce5a/index.html | 190 +++++ pages/42026f/index.html | 184 +++++ pages/43645f/index.html | 90 +++ pages/443e18/index.html | 118 +++ pages/4517cb/index.html | 855 ++++++++++++++++++++++ pages/46b6cc/index.html | 220 ++++++ pages/4784fc/index.html | 154 ++++ pages/48f6d3/index.html | 157 ++++ pages/4b18ae/index.html | 219 ++++++ pages/4bbcbf/index.html | 385 ++++++++++ pages/4c1e21/index.html | 93 +++ pages/4d2b48/index.html | 472 ++++++++++++ pages/4e6b65/index.html | 165 +++++ pages/526c58/index.html | 405 ++++++++++ pages/531d2b/index.html | 300 ++++++++ pages/541c9c/index.html | 817 +++++++++++++++++++++ pages/56dd3d/index.html | 234 ++++++ pages/577e7c/index.html | 224 ++++++ pages/58ae6a/index.html | 276 +++++++ pages/5944f7/index.html | 183 +++++ pages/5a5158/index.html | 128 ++++ pages/5c4b50/index.html | 375 ++++++++++ pages/5c8459/index.html | 410 +++++++++++ pages/5f806d/index.html | 90 +++ pages/5f9bf6/index.html | 187 +++++ pages/614119/index.html | 266 +++++++ pages/614da2/index.html | 333 +++++++++ pages/61cc74/index.html | 272 +++++++ pages/61d6a8/index.html | 218 ++++++ pages/63c550/index.html | 175 +++++ pages/650506/index.html | 804 ++++++++++++++++++++ pages/661407/index.html | 112 +++ pages/67358d/index.html | 172 +++++ pages/683c07/index.html | 152 ++++ pages/688a6b/index.html | 275 +++++++ pages/6caa6f/index.html | 181 +++++ pages/6d35fc/index.html | 331 +++++++++ pages/6f1cc2/index.html | 144 ++++ pages/70f890/index.html | 92 +++ pages/7776d6/index.html | 382 ++++++++++ pages/78c482/index.html | 191 +++++ pages/79a470/index.html | 91 +++ pages/79e15e/index.html | 294 ++++++++ pages/79e7e5/index.html | 311 ++++++++ pages/79efd2/index.html | 213 ++++++ pages/7b4815/index.html | 112 +++ pages/7b7220/index.html | 123 ++++ pages/7b86ec/index.html | 227 ++++++ pages/7bacc4/index.html | 116 +++ pages/7cd064/index.html | 148 ++++ pages/7e1f42/index.html | 179 +++++ pages/7f9fec/index.html | 195 +++++ pages/804129/index.html | 226 ++++++ pages/8102b2/index.html | 175 +++++ pages/810563/index.html | 112 +++ pages/814d28/index.html | 134 ++++ pages/8155cd/index.html | 182 +++++ pages/81ea4e/index.html | 227 ++++++ pages/832f44/index.html | 103 +++ pages/84a12e/index.html | 294 ++++++++ pages/861244/index.html | 138 ++++ pages/867207/index.html | 113 +++ pages/87435a/index.html | 163 +++++ pages/88eee8/index.html | 212 ++++++ pages/89a4c2/index.html | 134 ++++ pages/89ebcc/index.html | 193 +++++ pages/8ad3be/index.html | 250 +++++++ pages/8cf0b6/index.html | 293 ++++++++ pages/8e3184/index.html | 434 +++++++++++ pages/901745/index.html | 186 +++++ pages/997f68/index.html | 133 ++++ pages/9a078b/index.html | 296 ++++++++ pages/9a084c/index.html | 207 ++++++ pages/9b40a4/index.html | 256 +++++++ pages/9c63e1/index.html | 197 +++++ pages/9e247e/index.html | 155 ++++ pages/9f1d21/index.html | 125 ++++ pages/a03fed/index.html | 224 ++++++ pages/a0ceba/index.html | 121 +++ pages/a0e176/index.html | 220 ++++++ pages/a16954/index.html | 169 +++++ pages/a3228e/index.html | 298 ++++++++ pages/a39d04/index.html | 446 +++++++++++ pages/a52cff/index.html | 127 ++++ pages/a59099/index.html | 135 ++++ pages/a5eb80/index.html | 287 ++++++++ pages/a8e073/index.html | 184 +++++ pages/a90e8e/index.html | 210 ++++++ pages/a9e62e/index.html | 148 ++++ pages/ab3077/index.html | 228 ++++++ pages/ac13fc/index.html | 381 ++++++++++ pages/ad0098/index.html | 102 +++ pages/b0e486/index.html | 318 ++++++++ pages/b1aa38/index.html | 99 +++ pages/b1db2f/index.html | 198 +++++ pages/b3bc49/index.html | 198 +++++ pages/b46b7a/index.html | 91 +++ pages/b576c8/index.html | 505 +++++++++++++ pages/b5d9a7/index.html | 140 ++++ pages/b7cd59/index.html | 223 ++++++ pages/b960c3/index.html | 268 +++++++ pages/ba99fe/index.html | 519 +++++++++++++ pages/bbc079/index.html | 492 +++++++++++++ pages/bea8f9/index.html | 160 ++++ pages/beb840/index.html | 226 ++++++ pages/bfa843/index.html | 132 ++++ pages/bfc897/index.html | 203 ++++++ pages/c243aa/index.html | 112 +++ pages/c53d80/index.html | 183 +++++ pages/c5b883/index.html | 164 +++++ pages/c7c2c0/index.html | 230 ++++++ pages/c7d38a/index.html | 314 ++++++++ pages/cb27eb/index.html | 132 ++++ pages/cc54c3/index.html | 90 +++ pages/ccf9c0/index.html | 107 +++ pages/cd4aeb/index.html | 570 +++++++++++++++ pages/cd4fd1/index.html | 134 ++++ pages/d09c0b/index.html | 201 +++++ pages/d0a26b/index.html | 194 +++++ pages/d18561/index.html | 176 +++++ pages/d45f5d/index.html | 380 ++++++++++ pages/d4ac48/index.html | 136 ++++ pages/d85c7a/index.html | 719 ++++++++++++++++++ pages/daa62e/index.html | 184 +++++ pages/db2b0b/index.html | 641 ++++++++++++++++ pages/dd574d/index.html | 279 +++++++ pages/defa2f/index.html | 219 ++++++ pages/df8033/index.html | 181 +++++ pages/dfd4b7/index.html | 109 +++ pages/e06ece/index.html | 311 ++++++++ pages/e13e4e/index.html | 217 ++++++ pages/e18f51/index.html | 126 ++++ pages/e3339e/index.html | 280 +++++++ pages/e3a33a/index.html | 91 +++ pages/e3a5cf/index.html | 144 ++++ pages/e3acb4/index.html | 132 ++++ pages/e3eb8a/index.html | 271 +++++++ pages/e4ed73/index.html | 1095 ++++++++++++++++++++++++++++ pages/e6f2c4/index.html | 205 ++++++ pages/e87d2b/index.html | 213 ++++++ pages/e987b9/index.html | 266 +++++++ pages/e9d226/index.html | 201 +++++ pages/ea0f4e/index.html | 635 ++++++++++++++++ pages/ea5b48/index.html | 96 +++ pages/ea91be/index.html | 151 ++++ pages/ee17d1/index.html | 193 +++++ pages/f2b19d/index.html | 332 +++++++++ pages/f2ddfa/index.html | 147 ++++ pages/f63a8b/index.html | 159 ++++ pages/f6869d/index.html | 431 +++++++++++ pages/f6bde9/index.html | 104 +++ pages/f6d976/index.html | 243 ++++++ pages/f81235/index.html | 122 ++++ pages/f81fb5/index.html | 112 +++ pages/f98f41/index.html | 161 ++++ pages/fb0480/index.html | 137 ++++ pages/fc665a/index.html | 671 +++++++++++++++++ pages/fc879e/index.html | 246 +++++++ pages/fcfa28/index.html | 223 ++++++ pages/fe4c94/index.html | 164 +++++ pages/feeb2d/index.html | 216 ++++++ power/index.html | 89 +++ tags/index.html | 82 +++ web/index.html | 103 +++ 489 files changed, 54932 insertions(+) create mode 100644 404.html create mode 100644 about/GitLqr/index.html create mode 100644 about/LinXunFeng/index.html create mode 100644 ads.txt create mode 100644 app-ads.txt create mode 100644 archives/index.html create mode 100644 assets/css/0.styles.336be721.css create mode 100644 assets/img/search.83621669.svg create mode 100644 assets/js/10.91fde849.js create mode 100644 assets/js/100.f3dd0e2b.js create mode 100644 assets/js/101.21943b2d.js create mode 100644 assets/js/102.2bc77eeb.js create mode 100644 assets/js/103.5a9b8fdf.js create mode 100644 assets/js/104.9e5262e1.js create mode 100644 assets/js/105.f584d8aa.js create mode 100644 assets/js/106.9459ada7.js create mode 100644 assets/js/107.c6735fa5.js create mode 100644 assets/js/108.3c1de7a6.js create mode 100644 assets/js/109.4531e506.js create mode 100644 assets/js/11.2331f9b8.js create mode 100644 assets/js/110.1238c012.js create mode 100644 assets/js/111.8eded375.js create mode 100644 assets/js/112.0b4e5a43.js create mode 100644 assets/js/113.8ac5df7a.js create mode 100644 assets/js/114.e0f5ab9c.js create mode 100644 assets/js/115.f9a34f03.js create mode 100644 assets/js/116.e3223d76.js create mode 100644 assets/js/117.8634c1d7.js create mode 100644 assets/js/118.186377c7.js create mode 100644 assets/js/119.b9f44223.js create mode 100644 assets/js/12.e8428df9.js create mode 100644 assets/js/120.c0bd832d.js create mode 100644 assets/js/121.997c5f8d.js create mode 100644 assets/js/122.7f4b0ebb.js create mode 100644 assets/js/123.b52eea15.js create mode 100644 assets/js/124.e5c4591e.js create mode 100644 assets/js/125.aa402bfb.js create mode 100644 assets/js/126.e39ce489.js create mode 100644 assets/js/127.a6aa9d8a.js create mode 100644 assets/js/128.5f11baed.js create mode 100644 assets/js/129.1c49caac.js create mode 100644 assets/js/13.0f67029d.js create mode 100644 assets/js/130.b277ccc3.js create mode 100644 assets/js/131.5606c445.js create mode 100644 assets/js/132.cfe3f25f.js create mode 100644 assets/js/133.9616d190.js create mode 100644 assets/js/134.972bd9fb.js create mode 100644 assets/js/135.a29157ee.js create mode 100644 assets/js/136.43d150dd.js create mode 100644 assets/js/137.d3bfeadd.js create mode 100644 assets/js/138.31c4d124.js create mode 100644 assets/js/139.62bcfa96.js create mode 100644 assets/js/14.9854ca03.js create mode 100644 assets/js/140.add30aaf.js create mode 100644 assets/js/141.54f29e7a.js create mode 100644 assets/js/142.9f012dbb.js create mode 100644 assets/js/143.64c7beef.js create mode 100644 assets/js/144.cc019022.js create mode 100644 assets/js/145.caa7eeb2.js create mode 100644 assets/js/146.e9731e8e.js create mode 100644 assets/js/147.35098ba3.js create mode 100644 assets/js/148.b9c706c3.js create mode 100644 assets/js/149.73f1b672.js create mode 100644 assets/js/15.b489e01f.js create mode 100644 assets/js/150.cf00474f.js create mode 100644 assets/js/151.ac116fd1.js create mode 100644 assets/js/152.fe09d4d0.js create mode 100644 assets/js/153.6aea176e.js create mode 100644 assets/js/154.8228e5b4.js create mode 100644 assets/js/155.f1c0ec1c.js create mode 100644 assets/js/156.836f8c0b.js create mode 100644 assets/js/157.ee8ce36a.js create mode 100644 assets/js/158.2fa330aa.js create mode 100644 assets/js/159.384813a3.js create mode 100644 assets/js/16.1fd725ac.js create mode 100644 assets/js/160.efff9156.js create mode 100644 assets/js/161.6c996c2d.js create mode 100644 assets/js/162.0e55356e.js create mode 100644 assets/js/163.9288f368.js create mode 100644 assets/js/164.77c3a49b.js create mode 100644 assets/js/165.a0fd389c.js create mode 100644 assets/js/166.286a239f.js create mode 100644 assets/js/167.59756ff1.js create mode 100644 assets/js/168.fa83e66f.js create mode 100644 assets/js/169.bc6dd922.js create mode 100644 assets/js/17.471562b1.js create mode 100644 assets/js/170.8d0af8b3.js create mode 100644 assets/js/171.66d51e42.js create mode 100644 assets/js/172.21026f63.js create mode 100644 assets/js/173.a01dd23d.js create mode 100644 assets/js/174.4da76827.js create mode 100644 assets/js/175.9c09be49.js create mode 100644 assets/js/176.fd9291e1.js create mode 100644 assets/js/177.c3fdf3da.js create mode 100644 assets/js/178.b68b15a8.js create mode 100644 assets/js/179.b2f4fd5a.js create mode 100644 assets/js/18.6fe273b5.js create mode 100644 assets/js/180.4e654f2c.js create mode 100644 assets/js/181.cdf0f566.js create mode 100644 assets/js/182.320b5cb8.js create mode 100644 assets/js/183.258eee3b.js create mode 100644 assets/js/184.bf20e6a1.js create mode 100644 assets/js/185.ec7377be.js create mode 100644 assets/js/186.c4d4e096.js create mode 100644 assets/js/187.47b1a253.js create mode 100644 assets/js/188.aeddd400.js create mode 100644 assets/js/189.12ccf7cc.js create mode 100644 assets/js/19.40faf6f7.js create mode 100644 assets/js/190.866daee0.js create mode 100644 assets/js/191.5396824f.js create mode 100644 assets/js/192.2b856694.js create mode 100644 assets/js/193.aec16182.js create mode 100644 assets/js/194.024af9df.js create mode 100644 assets/js/195.ecf871dc.js create mode 100644 assets/js/196.f0b09f4f.js create mode 100644 assets/js/197.95e0485d.js create mode 100644 assets/js/198.68fe6ccc.js create mode 100644 assets/js/199.9f4ceccb.js create mode 100644 assets/js/2.fa15aff4.js create mode 100644 assets/js/20.9c50cd55.js create mode 100644 assets/js/200.c10d6697.js create mode 100644 assets/js/201.104c2fe8.js create mode 100644 assets/js/202.051fc2e2.js create mode 100644 assets/js/203.4ccb431e.js create mode 100644 assets/js/204.1e4af193.js create mode 100644 assets/js/205.7a5ac9ff.js create mode 100644 assets/js/206.cbeee199.js create mode 100644 assets/js/207.bf259bf2.js create mode 100644 assets/js/208.1e03267f.js create mode 100644 assets/js/209.0e585c42.js create mode 100644 assets/js/21.dab938ff.js create mode 100644 assets/js/210.ce8de1d4.js create mode 100644 assets/js/211.a190bfad.js create mode 100644 assets/js/212.411583ff.js create mode 100644 assets/js/213.8421540b.js create mode 100644 assets/js/214.15a34ffc.js create mode 100644 assets/js/215.8016ddfe.js create mode 100644 assets/js/216.dfaf6529.js create mode 100644 assets/js/217.56ae95ee.js create mode 100644 assets/js/218.2193df0b.js create mode 100644 assets/js/219.a721a7f1.js create mode 100644 assets/js/22.e3bbe947.js create mode 100644 assets/js/220.afd3ac7e.js create mode 100644 assets/js/221.d8d0d908.js create mode 100644 assets/js/222.adabf072.js create mode 100644 assets/js/223.c91ef62c.js create mode 100644 assets/js/224.2df4e374.js create mode 100644 assets/js/225.e7f77ec8.js create mode 100644 assets/js/226.3b853f48.js create mode 100644 assets/js/227.53d5dc87.js create mode 100644 assets/js/228.a58f8b25.js create mode 100644 assets/js/229.8c70f793.js create mode 100644 assets/js/23.fcbbd97e.js create mode 100644 assets/js/230.ecbcebee.js create mode 100644 assets/js/231.0b56153b.js create mode 100644 assets/js/232.b7b3d0b4.js create mode 100644 assets/js/233.f232d862.js create mode 100644 assets/js/234.04bd13a9.js create mode 100644 assets/js/235.bd29d721.js create mode 100644 assets/js/236.0691e9c4.js create mode 100644 assets/js/237.0ac1cc2c.js create mode 100644 assets/js/238.a9836170.js create mode 100644 assets/js/239.9a0cd159.js create mode 100644 assets/js/24.5ed1039b.js create mode 100644 assets/js/240.b95bbcb4.js create mode 100644 assets/js/241.89339429.js create mode 100644 assets/js/242.f17d88a2.js create mode 100644 assets/js/243.ba8f1a46.js create mode 100644 assets/js/25.04b0828d.js create mode 100644 assets/js/26.e10e2150.js create mode 100644 assets/js/27.d7d80ba6.js create mode 100644 assets/js/28.0ae7ec4a.js create mode 100644 assets/js/29.a3d6ab21.js create mode 100644 assets/js/3.5cf9d08f.js create mode 100644 assets/js/30.42612fcc.js create mode 100644 assets/js/31.563a23ab.js create mode 100644 assets/js/32.0cd95675.js create mode 100644 assets/js/33.4fe8f9da.js create mode 100644 assets/js/34.656dc501.js create mode 100644 assets/js/35.e7415926.js create mode 100644 assets/js/36.03ae92be.js create mode 100644 assets/js/37.e18c8add.js create mode 100644 assets/js/38.44ac0dde.js create mode 100644 assets/js/39.87f5b5df.js create mode 100644 assets/js/4.fe283263.js create mode 100644 assets/js/40.6bd5b71c.js create mode 100644 assets/js/41.b33fcd27.js create mode 100644 assets/js/42.ca77ab0a.js create mode 100644 assets/js/43.99eaf4fa.js create mode 100644 assets/js/44.4649fcde.js create mode 100644 assets/js/45.38401a83.js create mode 100644 assets/js/46.d2d9b1e6.js create mode 100644 assets/js/47.0209e99c.js create mode 100644 assets/js/48.47ad2024.js create mode 100644 assets/js/49.9e80286d.js create mode 100644 assets/js/5.f826da54.js create mode 100644 assets/js/50.0102ec23.js create mode 100644 assets/js/51.5b2cd17d.js create mode 100644 assets/js/52.168a55a9.js create mode 100644 assets/js/53.c33541bb.js create mode 100644 assets/js/54.adaef113.js create mode 100644 assets/js/55.7690e3e1.js create mode 100644 assets/js/56.d02001b6.js create mode 100644 assets/js/57.bebcd4b5.js create mode 100644 assets/js/58.4ce4bd83.js create mode 100644 assets/js/59.ef67227c.js create mode 100644 assets/js/6.c1094bfd.js create mode 100644 assets/js/60.859377cb.js create mode 100644 assets/js/61.3a1c277b.js create mode 100644 assets/js/62.cdfee049.js create mode 100644 assets/js/63.5f5de475.js create mode 100644 assets/js/64.ebb1fc9e.js create mode 100644 assets/js/65.3c560012.js create mode 100644 assets/js/66.fc96e675.js create mode 100644 assets/js/67.5eb144e1.js create mode 100644 assets/js/68.ef8efcb6.js create mode 100644 assets/js/69.ab9de634.js create mode 100644 assets/js/7.51b29a13.js create mode 100644 assets/js/70.60f8d760.js create mode 100644 assets/js/71.53b41eda.js create mode 100644 assets/js/72.c0aa0b43.js create mode 100644 assets/js/73.674ab9c7.js create mode 100644 assets/js/74.c9968e9e.js create mode 100644 assets/js/75.4aa6a762.js create mode 100644 assets/js/76.db0f1b6a.js create mode 100644 assets/js/77.1533e241.js create mode 100644 assets/js/78.3e8eaf07.js create mode 100644 assets/js/79.9934bc36.js create mode 100644 assets/js/8.10b56f02.js create mode 100644 assets/js/80.25941025.js create mode 100644 assets/js/81.6176dbdd.js create mode 100644 assets/js/82.efb50e5d.js create mode 100644 assets/js/83.562bf197.js create mode 100644 assets/js/84.b772a17c.js create mode 100644 assets/js/85.182832b9.js create mode 100644 assets/js/86.e5c39dda.js create mode 100644 assets/js/87.e22a17ac.js create mode 100644 assets/js/88.cf95790a.js create mode 100644 assets/js/89.72e8641b.js create mode 100644 assets/js/9.8e5c0c5b.js create mode 100644 assets/js/90.8d1a4842.js create mode 100644 assets/js/91.d103c54d.js create mode 100644 assets/js/92.54530957.js create mode 100644 assets/js/93.de2adb2d.js create mode 100644 assets/js/94.2c00c239.js create mode 100644 assets/js/95.8bc59368.js create mode 100644 assets/js/96.ad76297a.js create mode 100644 assets/js/97.c9c5f8b6.js create mode 100644 assets/js/98.581b98d0.js create mode 100644 assets/js/99.efe771cf.js create mode 100644 assets/js/app.87384e03.js create mode 100644 backend/index.html create mode 100644 categories/index.html create mode 100644 img/favicon.ico create mode 100644 index.html create mode 100644 js/global.js create mode 100644 mobile/index.html create mode 100644 more/index.html create mode 100644 note/designpattern/index.html create mode 100644 note/flutter/index.html create mode 100644 note/flutter/lxf/review/index.html create mode 100644 note/kotlin/index.html create mode 100644 pages/00e6eb/index.html create mode 100644 pages/022231/index.html create mode 100644 pages/030ff5/index.html create mode 100644 pages/0422cb/index.html create mode 100644 pages/04a87c/index.html create mode 100644 pages/05278d/index.html create mode 100644 pages/0790a0/index.html create mode 100644 pages/07f268/index.html create mode 100644 pages/084bc4/index.html create mode 100644 pages/084bf9/index.html create mode 100644 pages/087ea3/index.html create mode 100644 pages/0909a3/index.html create mode 100644 pages/091864/index.html create mode 100644 pages/0c1879/index.html create mode 100644 pages/0d47a0/index.html create mode 100644 pages/0d4d47/index.html create mode 100644 pages/0e461d/index.html create mode 100644 pages/0ee6a4/index.html create mode 100644 pages/0f2500/index.html create mode 100644 pages/0f3155/index.html create mode 100644 pages/0fcdc8/index.html create mode 100644 pages/10277d/index.html create mode 100644 pages/1084c4/index.html create mode 100644 pages/117e5d/index.html create mode 100644 pages/1195b1/index.html create mode 100644 pages/13365e/index.html create mode 100644 pages/13bf45/index.html create mode 100644 pages/13fb2b/index.html create mode 100644 pages/15960b/index.html create mode 100644 pages/15d73d/index.html create mode 100644 pages/1751fb/index.html create mode 100644 pages/17d6bb/index.html create mode 100644 pages/19fce1/index.html create mode 100644 pages/1af1e6/index.html create mode 100644 pages/1cb0c1/index.html create mode 100644 pages/1d7592/index.html create mode 100644 pages/2069ac/index.html create mode 100644 pages/21e20f/index.html create mode 100644 pages/23e976/index.html create mode 100644 pages/242c2b/index.html create mode 100644 pages/253dac/index.html create mode 100644 pages/26b199/index.html create mode 100644 pages/29287b/index.html create mode 100644 pages/294865/index.html create mode 100644 pages/2a27e8/index.html create mode 100644 pages/2afdff/index.html create mode 100644 pages/2c5ae4/index.html create mode 100644 pages/2ed5f7/index.html create mode 100644 pages/2ffb7f/index.html create mode 100644 pages/326c98/index.html create mode 100644 pages/328069/index.html create mode 100644 pages/32a06e/index.html create mode 100644 pages/334ef0/index.html create mode 100644 pages/337d0f/index.html create mode 100644 pages/3420a3/index.html create mode 100644 pages/34334d/index.html create mode 100644 pages/34e11b/index.html create mode 100644 pages/37adf1/index.html create mode 100644 pages/39525c/index.html create mode 100644 pages/398f01/index.html create mode 100644 pages/399558/index.html create mode 100644 pages/3a12b8/index.html create mode 100644 pages/3b59d7/index.html create mode 100644 pages/41ce5a/index.html create mode 100644 pages/42026f/index.html create mode 100644 pages/43645f/index.html create mode 100644 pages/443e18/index.html create mode 100644 pages/4517cb/index.html create mode 100644 pages/46b6cc/index.html create mode 100644 pages/4784fc/index.html create mode 100644 pages/48f6d3/index.html create mode 100644 pages/4b18ae/index.html create mode 100644 pages/4bbcbf/index.html create mode 100644 pages/4c1e21/index.html create mode 100644 pages/4d2b48/index.html create mode 100644 pages/4e6b65/index.html create mode 100644 pages/526c58/index.html create mode 100644 pages/531d2b/index.html create mode 100644 pages/541c9c/index.html create mode 100644 pages/56dd3d/index.html create mode 100644 pages/577e7c/index.html create mode 100644 pages/58ae6a/index.html create mode 100644 pages/5944f7/index.html create mode 100644 pages/5a5158/index.html create mode 100644 pages/5c4b50/index.html create mode 100644 pages/5c8459/index.html create mode 100644 pages/5f806d/index.html create mode 100644 pages/5f9bf6/index.html create mode 100644 pages/614119/index.html create mode 100644 pages/614da2/index.html create mode 100644 pages/61cc74/index.html create mode 100644 pages/61d6a8/index.html create mode 100644 pages/63c550/index.html create mode 100644 pages/650506/index.html create mode 100644 pages/661407/index.html create mode 100644 pages/67358d/index.html create mode 100644 pages/683c07/index.html create mode 100644 pages/688a6b/index.html create mode 100644 pages/6caa6f/index.html create mode 100644 pages/6d35fc/index.html create mode 100644 pages/6f1cc2/index.html create mode 100644 pages/70f890/index.html create mode 100644 pages/7776d6/index.html create mode 100644 pages/78c482/index.html create mode 100644 pages/79a470/index.html create mode 100644 pages/79e15e/index.html create mode 100644 pages/79e7e5/index.html create mode 100644 pages/79efd2/index.html create mode 100644 pages/7b4815/index.html create mode 100644 pages/7b7220/index.html create mode 100644 pages/7b86ec/index.html create mode 100644 pages/7bacc4/index.html create mode 100644 pages/7cd064/index.html create mode 100644 pages/7e1f42/index.html create mode 100644 pages/7f9fec/index.html create mode 100644 pages/804129/index.html create mode 100644 pages/8102b2/index.html create mode 100644 pages/810563/index.html create mode 100644 pages/814d28/index.html create mode 100644 pages/8155cd/index.html create mode 100644 pages/81ea4e/index.html create mode 100644 pages/832f44/index.html create mode 100644 pages/84a12e/index.html create mode 100644 pages/861244/index.html create mode 100644 pages/867207/index.html create mode 100644 pages/87435a/index.html create mode 100644 pages/88eee8/index.html create mode 100644 pages/89a4c2/index.html create mode 100644 pages/89ebcc/index.html create mode 100644 pages/8ad3be/index.html create mode 100644 pages/8cf0b6/index.html create mode 100644 pages/8e3184/index.html create mode 100644 pages/901745/index.html create mode 100644 pages/997f68/index.html create mode 100644 pages/9a078b/index.html create mode 100644 pages/9a084c/index.html create mode 100644 pages/9b40a4/index.html create mode 100644 pages/9c63e1/index.html create mode 100644 pages/9e247e/index.html create mode 100644 pages/9f1d21/index.html create mode 100644 pages/a03fed/index.html create mode 100644 pages/a0ceba/index.html create mode 100644 pages/a0e176/index.html create mode 100644 pages/a16954/index.html create mode 100644 pages/a3228e/index.html create mode 100644 pages/a39d04/index.html create mode 100644 pages/a52cff/index.html create mode 100644 pages/a59099/index.html create mode 100644 pages/a5eb80/index.html create mode 100644 pages/a8e073/index.html create mode 100644 pages/a90e8e/index.html create mode 100644 pages/a9e62e/index.html create mode 100644 pages/ab3077/index.html create mode 100644 pages/ac13fc/index.html create mode 100644 pages/ad0098/index.html create mode 100644 pages/b0e486/index.html create mode 100644 pages/b1aa38/index.html create mode 100644 pages/b1db2f/index.html create mode 100644 pages/b3bc49/index.html create mode 100644 pages/b46b7a/index.html create mode 100644 pages/b576c8/index.html create mode 100644 pages/b5d9a7/index.html create mode 100644 pages/b7cd59/index.html create mode 100644 pages/b960c3/index.html create mode 100644 pages/ba99fe/index.html create mode 100644 pages/bbc079/index.html create mode 100644 pages/bea8f9/index.html create mode 100644 pages/beb840/index.html create mode 100644 pages/bfa843/index.html create mode 100644 pages/bfc897/index.html create mode 100644 pages/c243aa/index.html create mode 100644 pages/c53d80/index.html create mode 100644 pages/c5b883/index.html create mode 100644 pages/c7c2c0/index.html create mode 100644 pages/c7d38a/index.html create mode 100644 pages/cb27eb/index.html create mode 100644 pages/cc54c3/index.html create mode 100644 pages/ccf9c0/index.html create mode 100644 pages/cd4aeb/index.html create mode 100644 pages/cd4fd1/index.html create mode 100644 pages/d09c0b/index.html create mode 100644 pages/d0a26b/index.html create mode 100644 pages/d18561/index.html create mode 100644 pages/d45f5d/index.html create mode 100644 pages/d4ac48/index.html create mode 100644 pages/d85c7a/index.html create mode 100644 pages/daa62e/index.html create mode 100644 pages/db2b0b/index.html create mode 100644 pages/dd574d/index.html create mode 100644 pages/defa2f/index.html create mode 100644 pages/df8033/index.html create mode 100644 pages/dfd4b7/index.html create mode 100644 pages/e06ece/index.html create mode 100644 pages/e13e4e/index.html create mode 100644 pages/e18f51/index.html create mode 100644 pages/e3339e/index.html create mode 100644 pages/e3a33a/index.html create mode 100644 pages/e3a5cf/index.html create mode 100644 pages/e3acb4/index.html create mode 100644 pages/e3eb8a/index.html create mode 100644 pages/e4ed73/index.html create mode 100644 pages/e6f2c4/index.html create mode 100644 pages/e87d2b/index.html create mode 100644 pages/e987b9/index.html create mode 100644 pages/e9d226/index.html create mode 100644 pages/ea0f4e/index.html create mode 100644 pages/ea5b48/index.html create mode 100644 pages/ea91be/index.html create mode 100644 pages/ee17d1/index.html create mode 100644 pages/f2b19d/index.html create mode 100644 pages/f2ddfa/index.html create mode 100644 pages/f63a8b/index.html create mode 100644 pages/f6869d/index.html create mode 100644 pages/f6bde9/index.html create mode 100644 pages/f6d976/index.html create mode 100644 pages/f81235/index.html create mode 100644 pages/f81fb5/index.html create mode 100644 pages/f98f41/index.html create mode 100644 pages/fb0480/index.html create mode 100644 pages/fc665a/index.html create mode 100644 pages/fc879e/index.html create mode 100644 pages/fcfa28/index.html create mode 100644 pages/fe4c94/index.html create mode 100644 pages/feeb2d/index.html create mode 100644 power/index.html create mode 100644 tags/index.html create mode 100644 web/index.html diff --git a/404.html b/404.html new file mode 100644 index 000000000..333dba964 --- /dev/null +++ b/404.html @@ -0,0 +1,33 @@ + + + + + + FSA全栈行动 + + + + + + + + + + + + + + + +
404
我是谁?我在哪?
返回首页
+ + + diff --git a/about/GitLqr/index.html b/about/GitLqr/index.html new file mode 100644 index 000000000..ad5f7b5b1 --- /dev/null +++ b/about/GitLqr/index.html @@ -0,0 +1,142 @@ + + + + + + 关于GitLqr | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/about/LinXunFeng/index.html b/about/LinXunFeng/index.html new file mode 100644 index 000000000..94c9a7e3c --- /dev/null +++ b/about/LinXunFeng/index.html @@ -0,0 +1,183 @@ + + + + + + 关于LinXunFeng | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/ads.txt b/ads.txt new file mode 100644 index 000000000..e8a7498b8 --- /dev/null +++ b/ads.txt @@ -0,0 +1 @@ +google.com, pub-3568502583266202, DIRECT, f08c47fec0942fa0 \ No newline at end of file diff --git a/app-ads.txt b/app-ads.txt new file mode 100644 index 000000000..e8a7498b8 --- /dev/null +++ b/app-ads.txt @@ -0,0 +1 @@ +google.com, pub-3568502583266202, DIRECT, f08c47fec0942fa0 \ No newline at end of file diff --git a/archives/index.html b/archives/index.html new file mode 100644 index 000000000..316ced126 --- /dev/null +++ b/archives/index.html @@ -0,0 +1,238 @@ + + + + + + 归档 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/assets/css/0.styles.336be721.css b/assets/css/0.styles.336be721.css new file mode 100644 index 000000000..26253c7eb --- /dev/null +++ b/assets/css/0.styles.336be721.css @@ -0,0 +1 @@ +@import url(//at.alicdn.com/t/font_1678482_4tbhmh589x.css);.code-copy{color:#aaa;fill:#aaa;font-size:14px;display:inline-block;cursor:pointer}div[class*=aside-code] aside .code-copy,div[class*=language-] pre .code-copy{position:absolute;z-index:1000;top:7px;right:35px;opacity:0;font-size:16px}div[class*=aside-code] aside:hover .code-copy,div[class*=language-] pre:hover .code-copy{opacity:1}.content pre,.content pre[class*=language-]{overflow-y:hidden}div[class*=language-] pre,div[class*=language-] pre[class*=language-]{position:static!important}div[class~=language-text]:before{content:"text"}div[class~=language-yml]:before{content:"yml"}div[class*=language-] pre{-webkit-user-select:text;-moz-user-select:text;user-select:text}p code{-webkit-user-select:all;-moz-user-select:all;user-select:all}@keyframes message-move-in{0%{opacity:0;transform:translateY(-100%)}to{opacity:1;transform:translateY(0)}}#message-container .message.move-in{animation:message-move-in .3s ease-in-out}@keyframes message-move-out{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-100%)}}#message-container .message.move-out{animation:message-move-out .3s ease-in-out;animation-fill-mode:forwards}#message-container .message{background:#fff;margin:10px 0;padding:0 10px;height:40px;box-shadow:0 0 10px 0 #ccc;font-size:14px;border-radius:3px;display:flex;align-items:center;transition:height .2s ease-in-out,margin .2s ease-in-out}#message-container{position:fixed;left:0;top:100px;right:0;display:flex;flex-direction:column;align-items:center}#message-container .message .text{color:#333;padding:0 20px 0 5px}.theme-code-block[data-v-4f1e9d0c]{display:none}.theme-code-block__active[data-v-4f1e9d0c]{display:block}.theme-code-block>pre[data-v-4f1e9d0c]{background-color:orange}@media (max-width:419px){.theme-code-group div[class*=language-][data-v-4f1e9d0c]{margin:0}}.theme-mode-light[data-v-2f5f1757]{--bodyBg:#f4f4f4;--mainBg:#fff;--sidebarBg:hsla(0,0%,100%,0.8);--blurBg:hsla(0,0%,100%,0.9);--customBlockBg:#f1f1f1;--textColor:#00323c;--textLightenColor:#0085ad;--borderColor:rgba(0,0,0,0.12);--codeBg:#f6f6f6;--codeColor:#525252}.theme-mode-light code[class*=language-][data-v-2f5f1757],.theme-mode-light pre[class*=language-][data-v-2f5f1757]{color:#000;background:none;text-shadow:0 1px #fff;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;hyphens:none}.theme-mode-light code[class*=language-][data-v-2f5f1757]::-moz-selection,.theme-mode-light code[class*=language-][data-v-2f5f1757] ::-moz-selection,.theme-mode-light pre[class*=language-][data-v-2f5f1757]::-moz-selection,.theme-mode-light pre[class*=language-][data-v-2f5f1757] ::-moz-selection{text-shadow:none;background:#b3d4fc}.theme-mode-light code[class*=language-][data-v-2f5f1757]::selection,.theme-mode-light code[class*=language-][data-v-2f5f1757] ::selection,.theme-mode-light pre[class*=language-][data-v-2f5f1757]::selection,.theme-mode-light pre[class*=language-][data-v-2f5f1757] ::selection{text-shadow:none;background:#b3d4fc}@media print{.theme-mode-light code[class*=language-][data-v-2f5f1757],.theme-mode-light pre[class*=language-][data-v-2f5f1757]{text-shadow:none}}.theme-mode-light pre[class*=language-][data-v-2f5f1757]{padding:1em;margin:.5em 0;overflow:auto}.theme-mode-light :not(pre)>code[class*=language-][data-v-2f5f1757],.theme-mode-light pre[class*=language-][data-v-2f5f1757]{background:#f5f2f0}.theme-mode-light :not(pre)>code[class*=language-][data-v-2f5f1757]{padding:.1em;border-radius:.3em;white-space:normal}.theme-mode-light .token.cdata[data-v-2f5f1757],.theme-mode-light .token.comment[data-v-2f5f1757],.theme-mode-light .token.doctype[data-v-2f5f1757],.theme-mode-light .token.prolog[data-v-2f5f1757]{color:#708090}.theme-mode-light .token.punctuation[data-v-2f5f1757]{color:#999}.theme-mode-light .namespace[data-v-2f5f1757]{opacity:.7}.theme-mode-light .token.boolean[data-v-2f5f1757],.theme-mode-light .token.constant[data-v-2f5f1757],.theme-mode-light .token.deleted[data-v-2f5f1757],.theme-mode-light .token.number[data-v-2f5f1757],.theme-mode-light .token.property[data-v-2f5f1757],.theme-mode-light .token.symbol[data-v-2f5f1757],.theme-mode-light .token.tag[data-v-2f5f1757]{color:#905}.theme-mode-light .token.attr-name[data-v-2f5f1757],.theme-mode-light .token.builtin[data-v-2f5f1757],.theme-mode-light .token.char[data-v-2f5f1757],.theme-mode-light .token.inserted[data-v-2f5f1757],.theme-mode-light .token.selector[data-v-2f5f1757],.theme-mode-light .token.string[data-v-2f5f1757]{color:#690}.theme-mode-light .language-css .token.string[data-v-2f5f1757],.theme-mode-light .style .token.string[data-v-2f5f1757],.theme-mode-light .token.entity[data-v-2f5f1757],.theme-mode-light .token.operator[data-v-2f5f1757],.theme-mode-light .token.url[data-v-2f5f1757]{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.theme-mode-light .token.atrule[data-v-2f5f1757],.theme-mode-light .token.attr-value[data-v-2f5f1757],.theme-mode-light .token.keyword[data-v-2f5f1757]{color:#07a}.theme-mode-light .token.class-name[data-v-2f5f1757],.theme-mode-light .token.function[data-v-2f5f1757]{color:#dd4a68}.theme-mode-light .token.important[data-v-2f5f1757],.theme-mode-light .token.regex[data-v-2f5f1757],.theme-mode-light .token.variable[data-v-2f5f1757]{color:#e90}.theme-mode-light .token.bold[data-v-2f5f1757],.theme-mode-light .token.important[data-v-2f5f1757]{font-weight:700}.theme-mode-light .token.italic[data-v-2f5f1757]{font-style:italic}.theme-mode-light .token.entity[data-v-2f5f1757]{cursor:help}.theme-mode-light div[class*=language-] .highlight-lines .highlighted[data-v-2f5f1757],.theme-mode-light div[class*=language-].line-numbers-mode .highlight-lines .highlighted[data-v-2f5f1757]:before{background-color:hsla(0,0%,78.4%,.4)}.theme-mode-dark[data-v-2f5f1757]{--bodyBg:#27272b;--mainBg:#1e1e22;--sidebarBg:rgba(30,30,34,0.8);--blurBg:rgba(30,30,34,0.8);--customBlockBg:#27272b;--textColor:#9b9baa;--textLightenColor:#0085ad;--borderColor:#30363d;--codeBg:#252526;--codeColor:#fff}.theme-mode-dark code[class*=language-][data-v-2f5f1757],.theme-mode-dark pre[class*=language-][data-v-2f5f1757]{color:#ccc;background:none;text-shadow: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;hyphens:none}.theme-mode-dark pre[class*=language-][data-v-2f5f1757]{padding:1em;margin:.5em 0;overflow:auto}.theme-mode-dark :not(pre)>code[class*=language-][data-v-2f5f1757],.theme-mode-dark pre[class*=language-][data-v-2f5f1757]{background:#2d2d2d}.theme-mode-dark :not(pre)>code[class*=language-][data-v-2f5f1757]{padding:.1em;border-radius:.3em;white-space:normal}.theme-mode-dark .token.block-comment[data-v-2f5f1757],.theme-mode-dark .token.cdata[data-v-2f5f1757],.theme-mode-dark .token.comment[data-v-2f5f1757],.theme-mode-dark .token.doctype[data-v-2f5f1757],.theme-mode-dark .token.prolog[data-v-2f5f1757]{color:#999}.theme-mode-dark .token.punctuation[data-v-2f5f1757]{color:#ccc}.theme-mode-dark .token.attr-name[data-v-2f5f1757],.theme-mode-dark .token.deleted[data-v-2f5f1757],.theme-mode-dark .token.namespace[data-v-2f5f1757],.theme-mode-dark .token.tag[data-v-2f5f1757]{color:#e2777a}.theme-mode-dark .token.function-name[data-v-2f5f1757]{color:#6196cc}.theme-mode-dark .token.boolean[data-v-2f5f1757],.theme-mode-dark .token.function[data-v-2f5f1757],.theme-mode-dark .token.number[data-v-2f5f1757]{color:#f08d49}.theme-mode-dark .token.class-name[data-v-2f5f1757],.theme-mode-dark .token.constant[data-v-2f5f1757],.theme-mode-dark .token.property[data-v-2f5f1757],.theme-mode-dark .token.symbol[data-v-2f5f1757]{color:#f8c555}.theme-mode-dark .token.atrule[data-v-2f5f1757],.theme-mode-dark .token.builtin[data-v-2f5f1757],.theme-mode-dark .token.important[data-v-2f5f1757],.theme-mode-dark .token.keyword[data-v-2f5f1757],.theme-mode-dark .token.selector[data-v-2f5f1757]{color:#cc99cd}.theme-mode-dark .token.attr-value[data-v-2f5f1757],.theme-mode-dark .token.char[data-v-2f5f1757],.theme-mode-dark .token.regex[data-v-2f5f1757],.theme-mode-dark .token.string[data-v-2f5f1757],.theme-mode-dark .token.variable[data-v-2f5f1757]{color:#7ec699}.theme-mode-dark .token.entity[data-v-2f5f1757],.theme-mode-dark .token.operator[data-v-2f5f1757],.theme-mode-dark .token.url[data-v-2f5f1757]{color:#67cdcc}.theme-mode-dark .language-css .token.string[data-v-2f5f1757],.theme-mode-dark .style .token.string[data-v-2f5f1757],.theme-mode-dark .token.entity[data-v-2f5f1757],.theme-mode-dark .token.operator[data-v-2f5f1757],.theme-mode-dark .token.url[data-v-2f5f1757]{background:none}.theme-mode-dark .token.bold[data-v-2f5f1757],.theme-mode-dark .token.important[data-v-2f5f1757]{font-weight:700}.theme-mode-dark .token.italic[data-v-2f5f1757]{font-style:italic}.theme-mode-dark .token.entity[data-v-2f5f1757]{cursor:help}.theme-mode-dark .token.inserted[data-v-2f5f1757]{color:green}.theme-mode-read[data-v-2f5f1757]{--bodyBg:#ececcc;--mainBg:#f5f5d5;--sidebarBg:rgba(245,245,213,0.8);--blurBg:rgba(245,245,213,0.9);--customBlockBg:#ececcc;--textColor:#704214;--textLightenColor:#963;--borderColor:rgba(0,0,0,0.15);--codeBg:#282c34;--codeColor:#fff}.theme-mode-read code[class*=language-][data-v-2f5f1757],.theme-mode-read pre[class*=language-][data-v-2f5f1757]{color:#ccc;background:none;text-shadow: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;hyphens:none}.theme-mode-read pre[class*=language-][data-v-2f5f1757]{padding:1em;margin:.5em 0;overflow:auto}.theme-mode-read :not(pre)>code[class*=language-][data-v-2f5f1757],.theme-mode-read pre[class*=language-][data-v-2f5f1757]{background:#2d2d2d}.theme-mode-read :not(pre)>code[class*=language-][data-v-2f5f1757]{padding:.1em;border-radius:.3em;white-space:normal}.theme-mode-read .token.block-comment[data-v-2f5f1757],.theme-mode-read .token.cdata[data-v-2f5f1757],.theme-mode-read .token.comment[data-v-2f5f1757],.theme-mode-read .token.doctype[data-v-2f5f1757],.theme-mode-read .token.prolog[data-v-2f5f1757]{color:#999}.theme-mode-read .token.punctuation[data-v-2f5f1757]{color:#ccc}.theme-mode-read .token.attr-name[data-v-2f5f1757],.theme-mode-read .token.deleted[data-v-2f5f1757],.theme-mode-read .token.namespace[data-v-2f5f1757],.theme-mode-read .token.tag[data-v-2f5f1757]{color:#e2777a}.theme-mode-read .token.function-name[data-v-2f5f1757]{color:#6196cc}.theme-mode-read .token.boolean[data-v-2f5f1757],.theme-mode-read .token.function[data-v-2f5f1757],.theme-mode-read .token.number[data-v-2f5f1757]{color:#f08d49}.theme-mode-read .token.class-name[data-v-2f5f1757],.theme-mode-read .token.constant[data-v-2f5f1757],.theme-mode-read .token.property[data-v-2f5f1757],.theme-mode-read .token.symbol[data-v-2f5f1757]{color:#f8c555}.theme-mode-read .token.atrule[data-v-2f5f1757],.theme-mode-read .token.builtin[data-v-2f5f1757],.theme-mode-read .token.important[data-v-2f5f1757],.theme-mode-read .token.keyword[data-v-2f5f1757],.theme-mode-read .token.selector[data-v-2f5f1757]{color:#cc99cd}.theme-mode-read .token.attr-value[data-v-2f5f1757],.theme-mode-read .token.char[data-v-2f5f1757],.theme-mode-read .token.regex[data-v-2f5f1757],.theme-mode-read .token.string[data-v-2f5f1757],.theme-mode-read .token.variable[data-v-2f5f1757]{color:#7ec699}.theme-mode-read .token.entity[data-v-2f5f1757],.theme-mode-read .token.operator[data-v-2f5f1757],.theme-mode-read .token.url[data-v-2f5f1757]{color:#67cdcc}.theme-mode-read .language-css .token.string[data-v-2f5f1757],.theme-mode-read .style .token.string[data-v-2f5f1757],.theme-mode-read .token.entity[data-v-2f5f1757],.theme-mode-read .token.operator[data-v-2f5f1757],.theme-mode-read .token.url[data-v-2f5f1757]{background:none}.theme-mode-read .token.bold[data-v-2f5f1757],.theme-mode-read .token.important[data-v-2f5f1757]{font-weight:700}.theme-mode-read .token.italic[data-v-2f5f1757]{font-style:italic}.theme-mode-read .token.entity[data-v-2f5f1757]{cursor:help}.theme-mode-read .token.inserted[data-v-2f5f1757]{color:green}.theme-style-line.theme-mode-light[data-v-2f5f1757]{--bodyBg:#fff}.theme-style-line.theme-mode-dark[data-v-2f5f1757]{--bodyBg:#1e1e22}.theme-style-line.theme-mode-read[data-v-2f5f1757]{--bodyBg:#f5f5d5}.theme-code-group[data-v-2f5f1757],.theme-code-group__nav[data-v-2f5f1757]{background-color:var(--codeBg);padding-bottom:22px;border-radius:6px;padding-left:10px;padding-top:10px}.theme-code-group__nav[data-v-2f5f1757]{margin-bottom:-35px}.theme-code-group__ul[data-v-2f5f1757]{margin:auto 0;padding-left:0;display:inline-flex;list-style:none}.theme-code-group__li[data-v-2f5f1757],.theme-code-group__nav-tab[data-v-2f5f1757]{border:0;padding:5px;cursor:pointer;background-color:transparent;font-size:.85em;line-height:1.4;color:var(--codeColor);font-weight:600;opacity:.85}.theme-code-group__nav-tab-active[data-v-2f5f1757]{border-bottom:1px solid #11a8cd;opacity:1}.pre-blank[data-v-2f5f1757]{color:#11a8cd}body .theme-vdoing-content code{color:var(--textLightenColor);padding:.25rem .5rem;margin:0;font-size:.9em;background-color:hsla(0,0%,39.2%,.08);border-radius:3px}body .theme-vdoing-content code .token.deleted{color:#ec5975}body .theme-vdoing-content code .token.inserted{color:#11a8cd}body .theme-vdoing-content pre,body .theme-vdoing-content pre[class*=language-]{line-height:1.4;padding:1.25rem 1.5rem;margin:.85rem 0;background-color:#282c34;border-radius:6px;overflow:auto}body .theme-vdoing-content pre[class*=language-] code,body .theme-vdoing-content pre code{color:var(--codeColor);padding:0;background-color:transparent;border-radius:0}div[class*=language-]{position:relative;background-color:var(--codeBg);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,.3)}div[class*=language-] pre,div[class*=language-] pre[class*=language-]{background:transparent;position:relative!important;z-index:1}div[class*=language-]:before{position:absolute;z-index:3;top:.8em;right:1em;font-size:.8rem;color:hsla(0,0%,58.8%,.7)}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:2.5rem;height:100%;background-color:rgba(0,0,0,.3)}div[class*=language-].line-numbers-mode pre{padding-left:3.5rem;vertical-align:middle}div[class*=language-].line-numbers-mode .line-numbers-wrapper{position:absolute;top:0;width:2.5rem;text-align:center;color:hsla(0,0%,49.8%,.5);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:2.5rem;height:100%;border-radius:6px 0 0 6px;border-right:1px solid var(--borderColor);background-color:var(--codeBg)}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:.2rem}.custom-block p{margin:0}.custom-block.danger,.custom-block.note,.custom-block.tip,.custom-block.warning{padding:.5rem 1.5rem;border-left-width:.5rem;border-left-style:solid;margin:1rem 0}.custom-block.tip{background-color:#f3f5f7;border-color:#42b983;color:#215d42}.custom-block.warning{background-color:#fff7d0;border-color:#e7c000;color:#6b5900}.custom-block.warning .custom-block-title{color:#b29400}.custom-block.warning a{color:var(--textColor)}.custom-block.danger{background-color:#ffe6e6;border-color:#c00;color:#4d0000}.custom-block.danger .custom-block-title{color:#900}.custom-block.danger a{color:var(--textColor)}.custom-block.note{background-color:#e8f5fa;border-color:#157bae;color:#0d4a68}.custom-block.right{color:var(--textColor);font-size:.9rem;text-align:right}.custom-block.theorem{margin:1rem 0;padding:.8rem 1.5rem;border-radius:2px;background-color:var(--customBlockBg)}.custom-block.theorem .title{font-weight:700;margin:.5rem 0}.custom-block.details{display:block;position:relative;border-radius:2px;margin:1em 0;padding:1.6em;background-color:var(--customBlockBg)}.custom-block.details p{margin:.8rem 0}.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}.custom-block.details summary:hover{color:#11a8cd}.theme-mode-dark .custom-block.warning{background-color:rgba(255,247,208,.2);color:#e7c000}.theme-mode-dark .custom-block.warning .custom-block-title{color:#ffdc2f}.theme-mode-dark .custom-block.tip{background-color:rgba(243,245,247,.2);color:#42b983}.theme-mode-dark .custom-block.danger{background-color:rgba(255,230,230,.4);color:maroon}.theme-mode-dark .custom-block.danger a{color:#11a8cd}.theme-mode-dark .custom-block.note{background-color:rgba(243,245,247,.2);color:#157bae}.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-vdoing-content:not(.custom){max-width:860px}.table-of-contents .badge{vertical-align:middle}.center-container{text-align:center}.center-container>h1,.center-container>h2,.center-container>h3,.center-container>h4,.center-container>h5,.center-container>h6{margin-top:-3.1rem;padding-top:4.6rem;margin-bottom:0}.center-container>h1 a.header-anchor,.center-container>h2 a.header-anchor,.center-container>h3 a.header-anchor,.center-container>h4 a.header-anchor,.center-container>h5 a.header-anchor,.center-container>h6 a.header-anchor{float:none;padding-right:0;margin-left:-.9rem}.cardListContainer{margin:.7rem 0}.cardListContainer>:not(.card-list){display:none}.cardListContainer .card-list{margin:-.35rem;display:flex;flex-wrap:wrap;align-items:flex-start}.cardListContainer .card-list .card-item{width:calc(33.33333% - .7rem);margin:.35rem;background:var(--bodyBg);border-radius:3px;color:var(--textColor);display:flex;box-shadow:1px 1px 2px 0 rgba(0,0,0,.06);transition:all .4s}.cardListContainer .card-list .card-item:hover{text-decoration:none;box-shadow:0 10px 20px -10px var(--randomColor,rgba(0,0,0,.15));transform:translateY(-3px) scale(1.01)}.cardListContainer .card-list .card-item:hover img{box-shadow:3px 2px 7px rgba(0,0,0,.15)}.cardListContainer .card-list .card-item:hover div p{text-shadow:3px 2px 5px rgba(0,0,0,.15)}.cardListContainer .card-list .card-item img{width:60px;height:60px;border-radius:50%;border:2px solid #fff;margin:1rem 0 1rem 1rem;box-shadow:3px 2px 5px rgba(0,0,0,.08);transition:all .4s}.cardListContainer .card-list .card-item div{flex:1;display:inline-block;float:right;padding:1rem 0}.cardListContainer .card-list .card-item div p{margin:0;padding:0 1rem;transition:text-shadow .4s;text-align:center}.cardListContainer .card-list .card-item div .name{margin:.2rem 0 .3rem}.cardListContainer .card-list .card-item div .desc{font-size:.8rem;line-height:1.1rem;opacity:.8;margin-bottom:.2rem}.cardListContainer .card-list .card-item.row-1{width:calc(100% - .7rem)}.cardListContainer .card-list .card-item.row-1 img{margin-left:2rem}.cardListContainer .card-list .card-item.row-2{width:calc(50% - .7rem)}.cardListContainer .card-list .card-item.row-2 img{margin-left:1.5rem}.cardListContainer .card-list .card-item.row-3{width:calc(33.33333% - .7rem)}.cardListContainer .card-list .card-item.row-4{width:calc(25% - .7rem)}.cardImgListContainer{margin:1rem 0}.cardImgListContainer>:not(.card-list){display:none}.cardImgListContainer .card-list{margin:-.5rem;display:flex;flex-wrap:wrap;align-items:flex-start}.cardImgListContainer .card-list .card-item{width:calc(33.33333% - 1rem);margin:.5rem;background:var(--mainBg);border:1px solid rgba(0,0,0,.1);box-sizing:border-box;border-radius:3px;overflow:hidden;color:var(--textColor);box-shadow:2px 2px 10px rgba(0,0,0,.04);display:flex;flex-direction:column;justify-content:flex-start;align-items:stretch;align-content:stretch;transition:all .4s}.cardImgListContainer .card-list .card-item:hover{box-shadow:1px 1px 20px rgba(0,0,0,.1);transform:translateY(-3px)}.cardImgListContainer .card-list .card-item .box-img{overflow:hidden;position:relative;background:#eee}.cardImgListContainer .card-list .card-item .box-img img{display:block;width:100%;height:100%;transition:all .3s}.cardImgListContainer .card-list .card-item a{color:var(--textColor);transition:color .3s}.cardImgListContainer .card-list .card-item a:hover{text-decoration:none}.cardImgListContainer .card-list .card-item .box-info{padding:.8rem 1rem}.cardImgListContainer .card-list .card-item .box-info p{margin:0}.cardImgListContainer .card-list .card-item .box-info .desc{margin-top:.3rem;opacity:.8;font-size:.9rem;line-height:1.1rem;overflow:hidden;white-space:normal;text-overflow:ellipsis;display:-webkit-box;-webkit-box-orient:vertical}.cardImgListContainer .card-list .card-item .box-footer{overflow:hidden;padding:.8rem 1rem;border-top:1px solid rgba(0,0,0,.1)}.cardImgListContainer .card-list .card-item .box-footer img{width:1.8rem;height:1.8rem;border-radius:50%;float:left}.cardImgListContainer .card-list .card-item .box-footer span{line-height:1.8rem;float:left;margin-left:.6rem;font-size:.8rem}.cardImgListContainer .card-list .card-item.row-1{width:calc(100% - 1rem)}.cardImgListContainer .card-list .card-item.row-2{width:calc(50% - 1rem)}.cardImgListContainer .card-list .card-item.row-3{width:calc(33.33333% - 1rem)}.cardImgListContainer .card-list .card-item.row-4{width:calc(25% - 1rem)}.theme-mode-dark .cardImgListContainer .card-list .card-item,.theme-mode-dark .cardImgListContainer .card-list .card-item .box-footer{border-color:var(--borderColor)}@media (max-width:900px){.cardListContainer .card-list .card-item.row-4{width:calc(33.33333% - .7rem)}.cardImgListContainer .card-list .card-item.row-4{width:calc(33.33333% - 1rem)}}@media (max-width:720px){.cardListContainer .card-list .card-item.row-3,.cardListContainer .card-list .card-item.row-4{width:calc(50% - .7rem)}.cardListContainer .card-list .card-item.row-3 img,.cardListContainer .card-list .card-item.row-4 img{margin-left:1.5rem}.cardImgListContainer .card-list .card-item.row-3,.cardImgListContainer .card-list .card-item.row-4{width:calc(50% - 1rem)}}@media (max-width:500px){.cardListContainer .card-list .card-item.row-1,.cardListContainer .card-list .card-item.row-2,.cardListContainer .card-list .card-item.row-3,.cardListContainer .card-list .card-item.row-4{width:calc(100% - .7rem)}.cardListContainer .card-list .card-item.row-1 img,.cardListContainer .card-list .card-item.row-2 img,.cardListContainer .card-list .card-item.row-3 img,.cardListContainer .card-list .card-item.row-4 img{margin-left:1.5rem}.cardImgListContainer .card-list .card-item.row-1,.cardImgListContainer .card-list .card-item.row-2,.cardImgListContainer .card-list .card-item.row-3,.cardImgListContainer .card-list .card-item.row-4{width:calc(100% - 1rem)}}body,html{padding:0;margin:0}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;-webkit-tap-highlight-color:transparent;font-size:16px;color:#2c3e50;background:var(--bodyBg)}a,button,input{outline:none;-webkit-tap-highlight-color:rgba(255,255,255,0);-webkit-focus-ring-color:transparent}@media (min-width:719px){::-webkit-scrollbar{width:6px;height:5px}::-webkit-scrollbar-track-piece{background-color:rgba(0,0,0,.15);-webkit-border-radius:3px}::-webkit-scrollbar-thumb:vertical{height:5px;background-color:rgba(0,0,0,.28);-webkit-border-radius:3px}::-webkit-scrollbar-thumb:horizontal{width:5px;background-color:rgba(0,0,0,.28);-webkit-border-radius:3px}}.card-box{border-radius:5px;background:var(--mainBg);box-shadow:0 0 4px 0 rgba(0,0,0,.1);transition:box-shadow .5s}.card-box:hover{box-shadow:0 1px 15px 0 rgba(0,0,0,.1)}@media (max-width:719px){.theme-style-line{margin-left:-1px;margin-right:-1px}}.theme-style-line .card-box{box-shadow:0 0;border:1px solid var(--borderColor)}.blur{-webkit-backdrop-filter:saturate(200%) blur(20px);backdrop-filter:saturate(200%) blur(20px)}.custom-page{min-height:calc(100vh - 3.6rem);padding-top:3.6rem;padding-bottom:.9rem}.custom-page .theme-vdoing-wrapper{margin:0 auto}body .search-box input{background-color:transparent;color:var(--textColor);border:1px solid var(--borderColor,#ccc)}@media (max-width:959px){body .search-box input{border-color:transparent}}.page{transition:padding .2s ease;padding-left:.8rem}.navbar{position:fixed;z-index:20;top:0;left:0;right:0;height:3.6rem;background-color:var(--blurBg);box-sizing:border-box;box-shadow:0 2px 5px rgba(0,0,0,.06)}.sidebar-mask{top:0;width:100vw;height:100vh}.sidebar-hover-trigger,.sidebar-mask{position:fixed;z-index:12;left:0;display:none}.sidebar-hover-trigger{top:8.1rem;bottom:0;width:24px}.sidebar{font-size:16px;background-color:var(--sidebarBg);width:18rem;position:fixed;z-index:13;margin:0;top:3.6rem;left:0;bottom:0;box-sizing:border-box;border-right:1px solid var(--borderColor);overflow-y:auto;transform:translateX(-100%);transition:transform .2s}@media (max-width:719px){.sidebar{background-color:var(--mainBg)}}.theme-vdoing-content:not(.custom){word-wrap:break-word}.theme-vdoing-content:not(.custom) a:hover{text-decoration:underline}.theme-vdoing-content:not(.custom) p.demo{padding:1rem 1.5rem;border:1px solid #ddd;border-radius:4px}.theme-vdoing-content:not(.custom) img{max-width:100%}.theme-vdoing-content.custom{padding:0;margin:0}.theme-vdoing-content.custom img{max-width:100%}a{font-weight:500;text-decoration:none}a,p a code{color:#11a8cd}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;opacity:.75;border-left:.2rem solid hsla(0,0%,39.2%,.3);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-vdoing-content:not(.custom)>h1,.theme-vdoing-content:not(.custom)>h2,.theme-vdoing-content:not(.custom)>h3,.theme-vdoing-content:not(.custom)>h4,.theme-vdoing-content:not(.custom)>h5,.theme-vdoing-content:not(.custom)>h6{margin-top:-3.1rem;padding-top:4.6rem;margin-bottom:0}.theme-vdoing-content:not(.custom)>h1:first-child,.theme-vdoing-content:not(.custom)>h2:first-child,.theme-vdoing-content:not(.custom)>h3:first-child,.theme-vdoing-content:not(.custom)>h4:first-child,.theme-vdoing-content:not(.custom)>h5:first-child,.theme-vdoing-content:not(.custom)>h6:first-child{margin-bottom:1rem}.theme-vdoing-content:not(.custom)>h1:first-child+.custom-block,.theme-vdoing-content:not(.custom)>h1:first-child+p,.theme-vdoing-content:not(.custom)>h1:first-child+pre,.theme-vdoing-content:not(.custom)>h2:first-child+.custom-block,.theme-vdoing-content:not(.custom)>h2:first-child+p,.theme-vdoing-content:not(.custom)>h2:first-child+pre,.theme-vdoing-content:not(.custom)>h3:first-child+.custom-block,.theme-vdoing-content:not(.custom)>h3:first-child+p,.theme-vdoing-content:not(.custom)>h3:first-child+pre,.theme-vdoing-content:not(.custom)>h4:first-child+.custom-block,.theme-vdoing-content:not(.custom)>h4:first-child+p,.theme-vdoing-content:not(.custom)>h4:first-child+pre,.theme-vdoing-content:not(.custom)>h5:first-child+.custom-block,.theme-vdoing-content:not(.custom)>h5:first-child+p,.theme-vdoing-content:not(.custom)>h5:first-child+pre,.theme-vdoing-content:not(.custom)>h6:first-child+.custom-block,.theme-vdoing-content:not(.custom)>h6:first-child+p,.theme-vdoing-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}.theme-vdoing-content:not(.custom)>.custom-block:first-child,.theme-vdoing-content:not(.custom)>p:first-child,.theme-vdoing-content:not(.custom)>pre:first-child{margin-top:2rem}h1{font-size:1.9rem}.theme-vdoing-content:not(.custom)>h1:first-child{display:none}h2{font-size:1.5rem;padding-bottom:.3rem;border-bottom:1px solid var(--borderColor)}h3{font-size:1.35rem}.page h4{font-size:1.25rem}.page h5{font-size:1.15rem}.page h6{font-size:1.05rem}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 var(--borderColor)}table{border-collapse:collapse;margin:1rem 0;overflow-x:auto;width:100%;display:inline-table}@media (max-width:719px){table{display:block}}tr{border-top:1px solid var(--borderColor)}tr:nth-child(2n){background-color:hsla(0,0%,58.8%,.1)}td,th{border:1px solid var(--borderColor);padding:.6em 1em}@media (max-width:719px){td,th{padding:.3em .5em}}td a,th a{word-break:break-all}.theme-container{color:var(--textColor);min-height:100vh}.theme-container.sidebar-open .sidebar-mask{display:block}.theme-container.no-navbar .theme-vdoing-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}}@media (max-width:719px){.sidebar{width:17.099999999999998rem}}@media (min-width:720px) and (max-width:959px){.sidebar{width:16.2rem}.theme-container.sidebar-open .page{padding-left:17rem!important}}@media (max-width:719px){.sidebar{top:0;height:100vh;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.sidebar-open .sidebar-mask{display:block}.theme-container.no-navbar .sidebar{padding-top:0}}@media (max-width:419px){h1{font-size:1.9rem}.theme-vdoing-content div[class*=language-]{margin:.85rem -1.5rem;border-radius:0}}@media (min-width:720px){.theme-container .sidebar-hover-trigger{display:block}.theme-container .sidebar-hover-trigger:hover~.sidebar,.theme-container:not(.sidebar-open) .sidebar-hover-trigger~.sidebar:hover{transform:translateX(0);z-index:100}.theme-container.sidebar-open .sidebar-mask{display:none}.theme-container.sidebar-open .sidebar{transform:translateX(0)}.theme-container.sidebar-open .sidebar-button{left:18rem}.theme-container.sidebar-open .page{padding-left:18.8rem;padding-right:.8rem}.theme-container.sidebar-open .sidebar-hover-trigger{display:none}.theme-container.have-rightmenu .page{padding-right:250px}.theme-container.no-sidebar .page{padding-left:0!important}.theme-container.no-sidebar .sidebar-hover-trigger{display:none}.theme-container.hide-navbar .sidebar-hover-trigger{top:4.5rem}.theme-container.hide-navbar .sidebar{top:0}.theme-container.no-sidebar .sidebar-button{display:none}}@media print{.buttons,.navbar,.sidebar{display:none}.page{padding-top:0!important}}@media (min-width:720px) and (max-width:959px){.theme-container.sidebar-open:not(.on-sidebar) .sidebar-button{left:12.6rem}}.gt-container .gt-ico-tip:after{content:"。( Win + . ) 或 ( ⌃ + ⌘ + ␣ ) 打开表情";color:#999;font-size:.8rem}.gt-container .gt-meta{border-color:var(--borderColor)!important}.gt-container .gt-comments-null{color:var(--textColor);opacity:.5}.gt-container .gt-header-textarea{color:var(--textColor);background:hsla(0,0%,70.6%,.1)!important}.gt-container .gt-btn{border-color:#11a8cd!important;background-color:#11a8cd!important}.gt-container .gt-btn-preview{background-color:hsla(0,0%,100%,0)!important;color:#11a8cd!important}.gt-container a{color:#11a8cd!important}.gt-container .gt-svg svg{fill:#11a8cd!important}.gt-container .gt-comment-admin .gt-comment-content,.gt-container .gt-comment-content{background-color:hsla(0,0%,58.8%,.1)!important}.gt-container .gt-comment-admin .gt-comment-content:hover,.gt-container .gt-comment-content:hover{box-shadow:0 0 25px hsla(0,0%,58.8%,.5)!important}.gt-container .gt-comment-admin .gt-comment-content .gt-comment-body,.gt-container .gt-comment-content .gt-comment-body{color:var(--textColor)!important}.qq{position:relative}.qq:after{content:"可撩";background:#11a8cd;color:#fff;padding:0 5px;border-radius:10px;font-size:12px;position:absolute;top:-4px;right:-35px;transform:scale(.85)}body .vuepress-plugin-demo-block__wrapper,body .vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__display{border-color:hsla(0,0%,62.7%,.3)}body .vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer:hover .vuepress-plugin-demo-block__expand:before{border-top-color:#11a8cd!important;border-bottom-color:#11a8cd!important}body .vuepress-plugin-demo-block__wrapper .vuepress-plugin-demo-block__footer:hover svg{fill:#11a8cd!important}#nprogress{pointer-events:none}#nprogress .bar{background:#11a8cd;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 #11a8cd,0 0 5px #11a8cd;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:#11a8cd transparent transparent #11a8cd;border-style:solid;border-width:2px;border-radius:50%;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}@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}.theme-mode-light[data-v-439bb2a8]{--bodyBg:#f4f4f4;--mainBg:#fff;--sidebarBg:hsla(0,0%,100%,0.8);--blurBg:hsla(0,0%,100%,0.9);--customBlockBg:#f1f1f1;--textColor:#00323c;--textLightenColor:#0085ad;--borderColor:rgba(0,0,0,0.12);--codeBg:#f6f6f6;--codeColor:#525252}.theme-mode-light code[class*=language-][data-v-439bb2a8],.theme-mode-light pre[class*=language-][data-v-439bb2a8]{color:#000;background:none;text-shadow:0 1px #fff;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;hyphens:none}.theme-mode-light code[class*=language-][data-v-439bb2a8]::-moz-selection,.theme-mode-light code[class*=language-][data-v-439bb2a8] ::-moz-selection,.theme-mode-light pre[class*=language-][data-v-439bb2a8]::-moz-selection,.theme-mode-light pre[class*=language-][data-v-439bb2a8] ::-moz-selection{text-shadow:none;background:#b3d4fc}.theme-mode-light code[class*=language-][data-v-439bb2a8]::selection,.theme-mode-light code[class*=language-][data-v-439bb2a8] ::selection,.theme-mode-light pre[class*=language-][data-v-439bb2a8]::selection,.theme-mode-light pre[class*=language-][data-v-439bb2a8] ::selection{text-shadow:none;background:#b3d4fc}@media print{.theme-mode-light code[class*=language-][data-v-439bb2a8],.theme-mode-light pre[class*=language-][data-v-439bb2a8]{text-shadow:none}}.theme-mode-light pre[class*=language-][data-v-439bb2a8]{padding:1em;margin:.5em 0;overflow:auto}.theme-mode-light :not(pre)>code[class*=language-][data-v-439bb2a8],.theme-mode-light pre[class*=language-][data-v-439bb2a8]{background:#f5f2f0}.theme-mode-light :not(pre)>code[class*=language-][data-v-439bb2a8]{padding:.1em;border-radius:.3em;white-space:normal}.theme-mode-light .token.cdata[data-v-439bb2a8],.theme-mode-light .token.comment[data-v-439bb2a8],.theme-mode-light .token.doctype[data-v-439bb2a8],.theme-mode-light .token.prolog[data-v-439bb2a8]{color:#708090}.theme-mode-light .token.punctuation[data-v-439bb2a8]{color:#999}.theme-mode-light .namespace[data-v-439bb2a8]{opacity:.7}.theme-mode-light .token.boolean[data-v-439bb2a8],.theme-mode-light .token.constant[data-v-439bb2a8],.theme-mode-light .token.deleted[data-v-439bb2a8],.theme-mode-light .token.number[data-v-439bb2a8],.theme-mode-light .token.property[data-v-439bb2a8],.theme-mode-light .token.symbol[data-v-439bb2a8],.theme-mode-light .token.tag[data-v-439bb2a8]{color:#905}.theme-mode-light .token.attr-name[data-v-439bb2a8],.theme-mode-light .token.builtin[data-v-439bb2a8],.theme-mode-light .token.char[data-v-439bb2a8],.theme-mode-light .token.inserted[data-v-439bb2a8],.theme-mode-light .token.selector[data-v-439bb2a8],.theme-mode-light .token.string[data-v-439bb2a8]{color:#690}.theme-mode-light .language-css .token.string[data-v-439bb2a8],.theme-mode-light .style .token.string[data-v-439bb2a8],.theme-mode-light .token.entity[data-v-439bb2a8],.theme-mode-light .token.operator[data-v-439bb2a8],.theme-mode-light .token.url[data-v-439bb2a8]{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.theme-mode-light .token.atrule[data-v-439bb2a8],.theme-mode-light .token.attr-value[data-v-439bb2a8],.theme-mode-light .token.keyword[data-v-439bb2a8]{color:#07a}.theme-mode-light .token.class-name[data-v-439bb2a8],.theme-mode-light .token.function[data-v-439bb2a8]{color:#dd4a68}.theme-mode-light .token.important[data-v-439bb2a8],.theme-mode-light .token.regex[data-v-439bb2a8],.theme-mode-light .token.variable[data-v-439bb2a8]{color:#e90}.theme-mode-light .token.bold[data-v-439bb2a8],.theme-mode-light .token.important[data-v-439bb2a8]{font-weight:700}.theme-mode-light .token.italic[data-v-439bb2a8]{font-style:italic}.theme-mode-light .token.entity[data-v-439bb2a8]{cursor:help}.theme-mode-light div[class*=language-] .highlight-lines .highlighted[data-v-439bb2a8],.theme-mode-light div[class*=language-].line-numbers-mode .highlight-lines .highlighted[data-v-439bb2a8]:before{background-color:hsla(0,0%,78.4%,.4)}.theme-mode-dark[data-v-439bb2a8]{--bodyBg:#27272b;--mainBg:#1e1e22;--sidebarBg:rgba(30,30,34,0.8);--blurBg:rgba(30,30,34,0.8);--customBlockBg:#27272b;--textColor:#9b9baa;--textLightenColor:#0085ad;--borderColor:#30363d;--codeBg:#252526;--codeColor:#fff}.theme-mode-dark code[class*=language-][data-v-439bb2a8],.theme-mode-dark pre[class*=language-][data-v-439bb2a8]{color:#ccc;background:none;text-shadow: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;hyphens:none}.theme-mode-dark pre[class*=language-][data-v-439bb2a8]{padding:1em;margin:.5em 0;overflow:auto}.theme-mode-dark :not(pre)>code[class*=language-][data-v-439bb2a8],.theme-mode-dark pre[class*=language-][data-v-439bb2a8]{background:#2d2d2d}.theme-mode-dark :not(pre)>code[class*=language-][data-v-439bb2a8]{padding:.1em;border-radius:.3em;white-space:normal}.theme-mode-dark .token.block-comment[data-v-439bb2a8],.theme-mode-dark .token.cdata[data-v-439bb2a8],.theme-mode-dark .token.comment[data-v-439bb2a8],.theme-mode-dark .token.doctype[data-v-439bb2a8],.theme-mode-dark .token.prolog[data-v-439bb2a8]{color:#999}.theme-mode-dark .token.punctuation[data-v-439bb2a8]{color:#ccc}.theme-mode-dark .token.attr-name[data-v-439bb2a8],.theme-mode-dark .token.deleted[data-v-439bb2a8],.theme-mode-dark .token.namespace[data-v-439bb2a8],.theme-mode-dark .token.tag[data-v-439bb2a8]{color:#e2777a}.theme-mode-dark .token.function-name[data-v-439bb2a8]{color:#6196cc}.theme-mode-dark .token.boolean[data-v-439bb2a8],.theme-mode-dark .token.function[data-v-439bb2a8],.theme-mode-dark .token.number[data-v-439bb2a8]{color:#f08d49}.theme-mode-dark .token.class-name[data-v-439bb2a8],.theme-mode-dark .token.constant[data-v-439bb2a8],.theme-mode-dark .token.property[data-v-439bb2a8],.theme-mode-dark .token.symbol[data-v-439bb2a8]{color:#f8c555}.theme-mode-dark .token.atrule[data-v-439bb2a8],.theme-mode-dark .token.builtin[data-v-439bb2a8],.theme-mode-dark .token.important[data-v-439bb2a8],.theme-mode-dark .token.keyword[data-v-439bb2a8],.theme-mode-dark .token.selector[data-v-439bb2a8]{color:#cc99cd}.theme-mode-dark .token.attr-value[data-v-439bb2a8],.theme-mode-dark .token.char[data-v-439bb2a8],.theme-mode-dark .token.regex[data-v-439bb2a8],.theme-mode-dark .token.string[data-v-439bb2a8],.theme-mode-dark .token.variable[data-v-439bb2a8]{color:#7ec699}.theme-mode-dark .token.entity[data-v-439bb2a8],.theme-mode-dark .token.operator[data-v-439bb2a8],.theme-mode-dark .token.url[data-v-439bb2a8]{color:#67cdcc}.theme-mode-dark .language-css .token.string[data-v-439bb2a8],.theme-mode-dark .style .token.string[data-v-439bb2a8],.theme-mode-dark .token.entity[data-v-439bb2a8],.theme-mode-dark .token.operator[data-v-439bb2a8],.theme-mode-dark .token.url[data-v-439bb2a8]{background:none}.theme-mode-dark .token.bold[data-v-439bb2a8],.theme-mode-dark .token.important[data-v-439bb2a8]{font-weight:700}.theme-mode-dark .token.italic[data-v-439bb2a8]{font-style:italic}.theme-mode-dark .token.entity[data-v-439bb2a8]{cursor:help}.theme-mode-dark .token.inserted[data-v-439bb2a8]{color:green}.theme-mode-read[data-v-439bb2a8]{--bodyBg:#ececcc;--mainBg:#f5f5d5;--sidebarBg:rgba(245,245,213,0.8);--blurBg:rgba(245,245,213,0.9);--customBlockBg:#ececcc;--textColor:#704214;--textLightenColor:#963;--borderColor:rgba(0,0,0,0.15);--codeBg:#282c34;--codeColor:#fff}.theme-mode-read code[class*=language-][data-v-439bb2a8],.theme-mode-read pre[class*=language-][data-v-439bb2a8]{color:#ccc;background:none;text-shadow: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;hyphens:none}.theme-mode-read pre[class*=language-][data-v-439bb2a8]{padding:1em;margin:.5em 0;overflow:auto}.theme-mode-read :not(pre)>code[class*=language-][data-v-439bb2a8],.theme-mode-read pre[class*=language-][data-v-439bb2a8]{background:#2d2d2d}.theme-mode-read :not(pre)>code[class*=language-][data-v-439bb2a8]{padding:.1em;border-radius:.3em;white-space:normal}.theme-mode-read .token.block-comment[data-v-439bb2a8],.theme-mode-read .token.cdata[data-v-439bb2a8],.theme-mode-read .token.comment[data-v-439bb2a8],.theme-mode-read .token.doctype[data-v-439bb2a8],.theme-mode-read .token.prolog[data-v-439bb2a8]{color:#999}.theme-mode-read .token.punctuation[data-v-439bb2a8]{color:#ccc}.theme-mode-read .token.attr-name[data-v-439bb2a8],.theme-mode-read .token.deleted[data-v-439bb2a8],.theme-mode-read .token.namespace[data-v-439bb2a8],.theme-mode-read .token.tag[data-v-439bb2a8]{color:#e2777a}.theme-mode-read .token.function-name[data-v-439bb2a8]{color:#6196cc}.theme-mode-read .token.boolean[data-v-439bb2a8],.theme-mode-read .token.function[data-v-439bb2a8],.theme-mode-read .token.number[data-v-439bb2a8]{color:#f08d49}.theme-mode-read .token.class-name[data-v-439bb2a8],.theme-mode-read .token.constant[data-v-439bb2a8],.theme-mode-read .token.property[data-v-439bb2a8],.theme-mode-read .token.symbol[data-v-439bb2a8]{color:#f8c555}.theme-mode-read .token.atrule[data-v-439bb2a8],.theme-mode-read .token.builtin[data-v-439bb2a8],.theme-mode-read .token.important[data-v-439bb2a8],.theme-mode-read .token.keyword[data-v-439bb2a8],.theme-mode-read .token.selector[data-v-439bb2a8]{color:#cc99cd}.theme-mode-read .token.attr-value[data-v-439bb2a8],.theme-mode-read .token.char[data-v-439bb2a8],.theme-mode-read .token.regex[data-v-439bb2a8],.theme-mode-read .token.string[data-v-439bb2a8],.theme-mode-read .token.variable[data-v-439bb2a8]{color:#7ec699}.theme-mode-read .token.entity[data-v-439bb2a8],.theme-mode-read .token.operator[data-v-439bb2a8],.theme-mode-read .token.url[data-v-439bb2a8]{color:#67cdcc}.theme-mode-read .language-css .token.string[data-v-439bb2a8],.theme-mode-read .style .token.string[data-v-439bb2a8],.theme-mode-read .token.entity[data-v-439bb2a8],.theme-mode-read .token.operator[data-v-439bb2a8],.theme-mode-read .token.url[data-v-439bb2a8]{background:none}.theme-mode-read .token.bold[data-v-439bb2a8],.theme-mode-read .token.important[data-v-439bb2a8]{font-weight:700}.theme-mode-read .token.italic[data-v-439bb2a8]{font-style:italic}.theme-mode-read .token.entity[data-v-439bb2a8]{cursor:help}.theme-mode-read .token.inserted[data-v-439bb2a8]{color:green}.theme-style-line.theme-mode-light[data-v-439bb2a8]{--bodyBg:#fff}.theme-style-line.theme-mode-dark[data-v-439bb2a8]{--bodyBg:#1e1e22}.theme-style-line.theme-mode-read[data-v-439bb2a8]{--bodyBg:#f5f5d5}.theme-vdoing-content[data-v-439bb2a8]{margin:3rem auto;padding:1.5rem}.theme-vdoing-content span[data-v-439bb2a8]{font-size:6rem;color:#11a8cd}.main-wrapper{margin:1.5rem auto 0;max-width:1100px;padding:0 .9rem;box-sizing:border-box;position:relative;display:flex}.main-wrapper .main-left{flex:1}.main-wrapper .main-left .theme-vdoing-content.card-box{padding:1rem 1.5rem;margin-bottom:.9rem}.main-wrapper .main-left .home-content{padding:1rem 1.5rem 0}.main-wrapper .main-right>*{width:245px;box-sizing:border-box}@media (max-width:900px){.main-wrapper .main-right>*{width:235px}}.main-wrapper .main-right .card-box{margin:0 0 .8rem .8rem;padding-top:.95rem;padding-bottom:.95rem}@media (max-width:719px){.main-wrapper{margin:.9rem 0;padding:0;display:block}.main-wrapper .main-left{width:100%}.main-wrapper .main-left .post-list{margin-bottom:3rem}.main-wrapper .main-left .post-list .post{border-radius:0}.main-wrapper .main-left .pagination{margin-bottom:3rem}.main-wrapper .main-right .blogger-wrapper{display:none}.main-wrapper .main-right .card-box{margin:0 0 .9rem;border-radius:0;width:100%}.theme-style-line .main-wrapper .main-right .card-box{margin:-1px 0 0}}.post-list{margin-bottom:4rem}.post-list .post{position:relative;padding:1rem 1.5rem;transition:all .3s;border-bottom:1px solid var(--borderColor)}.post-list .post:last-child{border-bottom:none}.post-list .post.post-leave-active{display:none}.post-list .post.post-enter{opacity:0;transform:translateX(-20px)}.post-list .post:before{position:absolute;top:-1px;right:0;font-size:2.5rem;color:#ff5722;opacity:.85}.post-list .post .title-wrapper a{color:var(--textColor)}.post-list .post .title-wrapper a:hover{color:#11a8cd}.post-list .post .title-wrapper h2{margin:.5rem 0;font-size:1.4rem;border:none}.post-list .post .title-wrapper h2 .title-tag{height:1.2rem;line-height:1.2rem;border:1px solid #ff5722;color:#ff5722;font-size:.8rem;padding:0 .35rem;border-radius:.2rem;margin-left:0;transform:translateY(-.15rem);display:inline-block}.post-list .post .title-wrapper h2 a{display:block}@media (max-width:719px){.post-list .post .title-wrapper h2 a{font-weight:400}}.post-list .post .title-wrapper .article-info>a,.post-list .post .title-wrapper .article-info>span{opacity:.7;font-size:.8rem;margin-right:1rem;cursor:pointer}.post-list .post .title-wrapper .article-info>a:before,.post-list .post .title-wrapper .article-info>span:before{margin-right:.3rem}.post-list .post .title-wrapper .article-info>a a,.post-list .post .title-wrapper .article-info>span a{margin:0}.post-list .post .title-wrapper .article-info>a a:not(:first-child):before,.post-list .post .title-wrapper .article-info>span a:not(:first-child):before{content:"/"}.post-list .post .title-wrapper .article-info .tags a:not(:first-child):before{content:"、"}.post-list .post .excerpt-wrapper{border-top:1px solid var(--borderColor);margin:.5rem 0;overflow:hidden}.post-list .post .excerpt-wrapper .excerpt{margin-bottom:.3rem;font-size:.92rem}.post-list .post .excerpt-wrapper .excerpt h1,.post-list .post .excerpt-wrapper .excerpt h2,.post-list .post .excerpt-wrapper .excerpt h3{display:none}.post-list .post .excerpt-wrapper .excerpt img{max-height:280px;max-width:100%!important;margin:0 auto}.post-list .post .excerpt-wrapper .readmore{float:right;margin-right:1rem;line-height:1rem}.post-list .post .excerpt-wrapper .readmore:before{float:right;font-size:.8rem;margin:.1rem 0 0 .2rem}.theme-style-line .post-list{border:1px solid var(--borderColor);border-radius:5px;overflow:hidden}.article-list{padding:1rem 2rem}@media (max-width:959px){.article-list{padding:1rem 1.5rem}}.article-list.no-article-list{display:none}.article-list .article-title{border-bottom:1px solid var(--borderColor);font-size:1.3rem;padding:1rem}.article-list .article-title a{font-size:1.2rem;color:var(--textColor);opacity:.9}.article-list .article-title a:before{margin-right:.4rem;font-size:1.1rem}.article-list .article-wrapper{overflow:hidden}.article-list .article-wrapper dl{border-bottom:1px dotted var(--borderColor);float:left;display:flex;padding:8px 0;margin:0;height:45px;width:100%}.article-list .article-wrapper dl dd{font-size:1.1rem;color:#f17229;width:50px;text-align:center;margin:0;line-height:45px}.article-list .article-wrapper dl dt{flex:1;display:flex}.article-list .article-wrapper dl dt a{color:var(--textColor);flex:1;display:flex;height:45px;align-items:center;font-weight:400}.article-list .article-wrapper dl dt a div{overflow:hidden;white-space:normal;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}.article-list .article-wrapper dl dt a div .title-tag{border:1px solid #ff5722;color:#ff5722;font-size:.8rem;padding:0 .35rem;border-radius:.2rem;margin-left:0;transform:translateY(-.05rem);display:inline-block}.article-list .article-wrapper dl dt a:hover{text-decoration:underline}.article-list .article-wrapper dl dt a.more{color:#11a8cd}.article-list .article-wrapper dl dt .date{width:50px;margin-right:15px;color:#999;text-align:right;font-size:.9rem;line-height:45px}.pagination{position:relative;height:60px;text-align:center}@media (max-width:720px){.pagination{margin-left:1px;margin-right:1px}}.pagination span{line-height:1rem;opacity:.9;cursor:pointer}.pagination span:hover{color:#11a8cd}.pagination span.ellipsis{opacity:.5}.pagination span.ellipsis:before{content:"...";font-size:1.2rem}@media (any-hover:hover){.pagination span.ellipsis.ell-two:hover:before{content:"«"}.pagination span.ellipsis.ell-four:hover:before{content:"»"}}.pagination>span{position:absolute;top:0;padding:1rem 1.2rem;font-size:.95rem}.pagination>span:before{font-size:.4rem}.pagination>span.disabled{color:hsla(0,0%,49%,.5)}.pagination>span.prev{left:0}.pagination>span.prev:before{margin-right:.3rem}.pagination>span.next{right:0}.pagination>span.next:before{float:right;margin-left:.3rem}.pagination>span p{display:inline;line-height:.95rem}.pagination .pagination-list span{display:inline-block;width:2.5rem;height:2.5rem;line-height:2.5rem;margin:.3rem}.pagination .pagination-list span.active{background:#11a8cd;color:var(--mainBg)}@media (max-width:800px){.pagination>span{padding:1rem 1.5rem}.pagination>span p{display:none}}@media (max-width:719px){.pagination>span{padding:.9rem 1.5rem}.pagination .pagination-list span{width:2.3rem;height:2.3rem;line-height:2.3rem;margin:.25rem}}@media (max-width:390px){.pagination>span{padding:.8rem 1.3rem}.pagination .pagination-list span{width:2rem;height:2rem;line-height:2rem;margin:.3rem .1rem .1rem}}.blogger-wrapper{height:auto;display:inline-table;padding-top:0!important;overflow:hidden}.blogger-wrapper .avatar{width:100%;overflow:hidden}.blogger-wrapper .avatar img{width:100%;height:100%}.blogger-wrapper .icons{border-top:none;height:35px;line-height:35px}.blogger-wrapper .icons a{font-size:20px;width:33%;color:var(--textColor);display:block;float:left;text-align:center;opacity:.8}.blogger-wrapper .icons a:hover{color:#11a8cd}.blogger-wrapper .blogger{padding:.3rem .95rem 0}.blogger-wrapper .blogger .name{font-size:1.3rem;display:block;margin-bottom:6px}.blogger-wrapper .blogger .slogan{color:var(--textColor)}.categories-wrapper .title{color:var(--textColor);opacity:.9;font-size:1.2rem;padding:0 .95rem}.categories-wrapper .title:before{margin-right:.3rem}.categories-wrapper .categories{margin-top:.6rem}.categories-wrapper .categories a{display:block;padding:8px .95rem 7px;color:var(--textColor);opacity:.8;font-size:.95rem;line-height:.95rem;position:relative;transition:all .2s;border-left:2px solid transparent;margin-top:-1px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}@media (max-width:719px){.categories-wrapper .categories a{font-weight:400}}.categories-wrapper .categories a:not(.active):hover{color:#11a8cd;background:#f8f8f8;border-color:#11a8cd}.categories-wrapper .categories a:not(.active):hover span{opacity:.8}.categories-wrapper .categories a span{float:right;background-color:var(--textColor);color:var(--mainBg);border-radius:8px;padding:0 .13rem;min-width:1rem;height:1rem;line-height:1rem;font-size:.6rem;text-align:center;opacity:.6;transition:opacity .3s}.categories-wrapper .categories a.active{background:#11a8cd;color:var(--mainBg);padding-left:.8rem;border-radius:1px;border-color:transparent}.theme-mode-dark .categories-wrapper .categories a:not(.active):hover,.theme-mode-read .categories-wrapper .categories a:not(.active):hover{background:var(--customBlockBg)}.tags-wrapper{padding:0 .95rem}.tags-wrapper .title{color:var(--textColor);opacity:.9;font-size:1.2rem}.tags-wrapper .title:before{margin-right:.3rem}.tags-wrapper .tags{text-align:justify;padding:.8rem .5rem .5rem;margin:0 -.5rem -.5rem}.tags-wrapper .tags a{opacity:.8;display:inline-block;padding:.2rem .4rem;transition:all .4s;background-color:var(--textColor);color:var(--mainBg);border-radius:3px;margin:0 .3rem .5rem 0;min-width:2rem;height:1rem;line-height:1rem;font-size:.8rem;text-align:center}@media (max-width:719px){.tags-wrapper .tags a{font-weight:400}}.tags-wrapper .tags a:hover{opacity:1;transform:scale(1.1)}.tags-wrapper .tags a.active{box-shadow:0 5px 10px -5px var(--randomColor,rgba(0,0,0,.15));transform:scale(1.22);opacity:1}.tags-wrapper .tags a.active:hover{text-decoration:none}.theme-mode-light[data-v-46a3a687]{--bodyBg:#f4f4f4;--mainBg:#fff;--sidebarBg:hsla(0,0%,100%,0.8);--blurBg:hsla(0,0%,100%,0.9);--customBlockBg:#f1f1f1;--textColor:#00323c;--textLightenColor:#0085ad;--borderColor:rgba(0,0,0,0.12);--codeBg:#f6f6f6;--codeColor:#525252}.theme-mode-light code[class*=language-][data-v-46a3a687],.theme-mode-light pre[class*=language-][data-v-46a3a687]{color:#000;background:none;text-shadow:0 1px #fff;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;hyphens:none}.theme-mode-light code[class*=language-][data-v-46a3a687]::-moz-selection,.theme-mode-light code[class*=language-][data-v-46a3a687] ::-moz-selection,.theme-mode-light pre[class*=language-][data-v-46a3a687]::-moz-selection,.theme-mode-light pre[class*=language-][data-v-46a3a687] ::-moz-selection{text-shadow:none;background:#b3d4fc}.theme-mode-light code[class*=language-][data-v-46a3a687]::selection,.theme-mode-light code[class*=language-][data-v-46a3a687] ::selection,.theme-mode-light pre[class*=language-][data-v-46a3a687]::selection,.theme-mode-light pre[class*=language-][data-v-46a3a687] ::selection{text-shadow:none;background:#b3d4fc}@media print{.theme-mode-light code[class*=language-][data-v-46a3a687],.theme-mode-light pre[class*=language-][data-v-46a3a687]{text-shadow:none}}.theme-mode-light pre[class*=language-][data-v-46a3a687]{padding:1em;margin:.5em 0;overflow:auto}.theme-mode-light :not(pre)>code[class*=language-][data-v-46a3a687],.theme-mode-light pre[class*=language-][data-v-46a3a687]{background:#f5f2f0}.theme-mode-light :not(pre)>code[class*=language-][data-v-46a3a687]{padding:.1em;border-radius:.3em;white-space:normal}.theme-mode-light .token.cdata[data-v-46a3a687],.theme-mode-light .token.comment[data-v-46a3a687],.theme-mode-light .token.doctype[data-v-46a3a687],.theme-mode-light .token.prolog[data-v-46a3a687]{color:#708090}.theme-mode-light .token.punctuation[data-v-46a3a687]{color:#999}.theme-mode-light .namespace[data-v-46a3a687]{opacity:.7}.theme-mode-light .token.boolean[data-v-46a3a687],.theme-mode-light .token.constant[data-v-46a3a687],.theme-mode-light .token.deleted[data-v-46a3a687],.theme-mode-light .token.number[data-v-46a3a687],.theme-mode-light .token.property[data-v-46a3a687],.theme-mode-light .token.symbol[data-v-46a3a687],.theme-mode-light .token.tag[data-v-46a3a687]{color:#905}.theme-mode-light .token.attr-name[data-v-46a3a687],.theme-mode-light .token.builtin[data-v-46a3a687],.theme-mode-light .token.char[data-v-46a3a687],.theme-mode-light .token.inserted[data-v-46a3a687],.theme-mode-light .token.selector[data-v-46a3a687],.theme-mode-light .token.string[data-v-46a3a687]{color:#690}.theme-mode-light .language-css .token.string[data-v-46a3a687],.theme-mode-light .style .token.string[data-v-46a3a687],.theme-mode-light .token.entity[data-v-46a3a687],.theme-mode-light .token.operator[data-v-46a3a687],.theme-mode-light .token.url[data-v-46a3a687]{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.theme-mode-light .token.atrule[data-v-46a3a687],.theme-mode-light .token.attr-value[data-v-46a3a687],.theme-mode-light .token.keyword[data-v-46a3a687]{color:#07a}.theme-mode-light .token.class-name[data-v-46a3a687],.theme-mode-light .token.function[data-v-46a3a687]{color:#dd4a68}.theme-mode-light .token.important[data-v-46a3a687],.theme-mode-light .token.regex[data-v-46a3a687],.theme-mode-light .token.variable[data-v-46a3a687]{color:#e90}.theme-mode-light .token.bold[data-v-46a3a687],.theme-mode-light .token.important[data-v-46a3a687]{font-weight:700}.theme-mode-light .token.italic[data-v-46a3a687]{font-style:italic}.theme-mode-light .token.entity[data-v-46a3a687]{cursor:help}.theme-mode-light div[class*=language-] .highlight-lines .highlighted[data-v-46a3a687],.theme-mode-light div[class*=language-].line-numbers-mode .highlight-lines .highlighted[data-v-46a3a687]:before{background-color:hsla(0,0%,78.4%,.4)}.theme-mode-dark[data-v-46a3a687]{--bodyBg:#27272b;--mainBg:#1e1e22;--sidebarBg:rgba(30,30,34,0.8);--blurBg:rgba(30,30,34,0.8);--customBlockBg:#27272b;--textColor:#9b9baa;--textLightenColor:#0085ad;--borderColor:#30363d;--codeBg:#252526;--codeColor:#fff}.theme-mode-dark code[class*=language-][data-v-46a3a687],.theme-mode-dark pre[class*=language-][data-v-46a3a687]{color:#ccc;background:none;text-shadow: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;hyphens:none}.theme-mode-dark pre[class*=language-][data-v-46a3a687]{padding:1em;margin:.5em 0;overflow:auto}.theme-mode-dark :not(pre)>code[class*=language-][data-v-46a3a687],.theme-mode-dark pre[class*=language-][data-v-46a3a687]{background:#2d2d2d}.theme-mode-dark :not(pre)>code[class*=language-][data-v-46a3a687]{padding:.1em;border-radius:.3em;white-space:normal}.theme-mode-dark .token.block-comment[data-v-46a3a687],.theme-mode-dark .token.cdata[data-v-46a3a687],.theme-mode-dark .token.comment[data-v-46a3a687],.theme-mode-dark .token.doctype[data-v-46a3a687],.theme-mode-dark .token.prolog[data-v-46a3a687]{color:#999}.theme-mode-dark .token.punctuation[data-v-46a3a687]{color:#ccc}.theme-mode-dark .token.attr-name[data-v-46a3a687],.theme-mode-dark .token.deleted[data-v-46a3a687],.theme-mode-dark .token.namespace[data-v-46a3a687],.theme-mode-dark .token.tag[data-v-46a3a687]{color:#e2777a}.theme-mode-dark .token.function-name[data-v-46a3a687]{color:#6196cc}.theme-mode-dark .token.boolean[data-v-46a3a687],.theme-mode-dark .token.function[data-v-46a3a687],.theme-mode-dark .token.number[data-v-46a3a687]{color:#f08d49}.theme-mode-dark .token.class-name[data-v-46a3a687],.theme-mode-dark .token.constant[data-v-46a3a687],.theme-mode-dark .token.property[data-v-46a3a687],.theme-mode-dark .token.symbol[data-v-46a3a687]{color:#f8c555}.theme-mode-dark .token.atrule[data-v-46a3a687],.theme-mode-dark .token.builtin[data-v-46a3a687],.theme-mode-dark .token.important[data-v-46a3a687],.theme-mode-dark .token.keyword[data-v-46a3a687],.theme-mode-dark .token.selector[data-v-46a3a687]{color:#cc99cd}.theme-mode-dark .token.attr-value[data-v-46a3a687],.theme-mode-dark .token.char[data-v-46a3a687],.theme-mode-dark .token.regex[data-v-46a3a687],.theme-mode-dark .token.string[data-v-46a3a687],.theme-mode-dark .token.variable[data-v-46a3a687]{color:#7ec699}.theme-mode-dark .token.entity[data-v-46a3a687],.theme-mode-dark .token.operator[data-v-46a3a687],.theme-mode-dark .token.url[data-v-46a3a687]{color:#67cdcc}.theme-mode-dark .language-css .token.string[data-v-46a3a687],.theme-mode-dark .style .token.string[data-v-46a3a687],.theme-mode-dark .token.entity[data-v-46a3a687],.theme-mode-dark .token.operator[data-v-46a3a687],.theme-mode-dark .token.url[data-v-46a3a687]{background:none}.theme-mode-dark .token.bold[data-v-46a3a687],.theme-mode-dark .token.important[data-v-46a3a687]{font-weight:700}.theme-mode-dark .token.italic[data-v-46a3a687]{font-style:italic}.theme-mode-dark .token.entity[data-v-46a3a687]{cursor:help}.theme-mode-dark .token.inserted[data-v-46a3a687]{color:green}.theme-mode-read[data-v-46a3a687]{--bodyBg:#ececcc;--mainBg:#f5f5d5;--sidebarBg:rgba(245,245,213,0.8);--blurBg:rgba(245,245,213,0.9);--customBlockBg:#ececcc;--textColor:#704214;--textLightenColor:#963;--borderColor:rgba(0,0,0,0.15);--codeBg:#282c34;--codeColor:#fff}.theme-mode-read code[class*=language-][data-v-46a3a687],.theme-mode-read pre[class*=language-][data-v-46a3a687]{color:#ccc;background:none;text-shadow: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;hyphens:none}.theme-mode-read pre[class*=language-][data-v-46a3a687]{padding:1em;margin:.5em 0;overflow:auto}.theme-mode-read :not(pre)>code[class*=language-][data-v-46a3a687],.theme-mode-read pre[class*=language-][data-v-46a3a687]{background:#2d2d2d}.theme-mode-read :not(pre)>code[class*=language-][data-v-46a3a687]{padding:.1em;border-radius:.3em;white-space:normal}.theme-mode-read .token.block-comment[data-v-46a3a687],.theme-mode-read .token.cdata[data-v-46a3a687],.theme-mode-read .token.comment[data-v-46a3a687],.theme-mode-read .token.doctype[data-v-46a3a687],.theme-mode-read .token.prolog[data-v-46a3a687]{color:#999}.theme-mode-read .token.punctuation[data-v-46a3a687]{color:#ccc}.theme-mode-read .token.attr-name[data-v-46a3a687],.theme-mode-read .token.deleted[data-v-46a3a687],.theme-mode-read .token.namespace[data-v-46a3a687],.theme-mode-read .token.tag[data-v-46a3a687]{color:#e2777a}.theme-mode-read .token.function-name[data-v-46a3a687]{color:#6196cc}.theme-mode-read .token.boolean[data-v-46a3a687],.theme-mode-read .token.function[data-v-46a3a687],.theme-mode-read .token.number[data-v-46a3a687]{color:#f08d49}.theme-mode-read .token.class-name[data-v-46a3a687],.theme-mode-read .token.constant[data-v-46a3a687],.theme-mode-read .token.property[data-v-46a3a687],.theme-mode-read .token.symbol[data-v-46a3a687]{color:#f8c555}.theme-mode-read .token.atrule[data-v-46a3a687],.theme-mode-read .token.builtin[data-v-46a3a687],.theme-mode-read .token.important[data-v-46a3a687],.theme-mode-read .token.keyword[data-v-46a3a687],.theme-mode-read .token.selector[data-v-46a3a687]{color:#cc99cd}.theme-mode-read .token.attr-value[data-v-46a3a687],.theme-mode-read .token.char[data-v-46a3a687],.theme-mode-read .token.regex[data-v-46a3a687],.theme-mode-read .token.string[data-v-46a3a687],.theme-mode-read .token.variable[data-v-46a3a687]{color:#7ec699}.theme-mode-read .token.entity[data-v-46a3a687],.theme-mode-read .token.operator[data-v-46a3a687],.theme-mode-read .token.url[data-v-46a3a687]{color:#67cdcc}.theme-mode-read .language-css .token.string[data-v-46a3a687],.theme-mode-read .style .token.string[data-v-46a3a687],.theme-mode-read .token.entity[data-v-46a3a687],.theme-mode-read .token.operator[data-v-46a3a687],.theme-mode-read .token.url[data-v-46a3a687]{background:none}.theme-mode-read .token.bold[data-v-46a3a687],.theme-mode-read .token.important[data-v-46a3a687]{font-weight:700}.theme-mode-read .token.italic[data-v-46a3a687]{font-style:italic}.theme-mode-read .token.entity[data-v-46a3a687]{cursor:help}.theme-mode-read .token.inserted[data-v-46a3a687]{color:green}.theme-style-line.theme-mode-light[data-v-46a3a687]{--bodyBg:#fff}.theme-style-line.theme-mode-dark[data-v-46a3a687]{--bodyBg:#1e1e22}.theme-style-line.theme-mode-read[data-v-46a3a687]{--bodyBg:#f5f5d5}.home-wrapper .banner[data-v-46a3a687]{width:100%;min-height:450px;margin-top:3.6rem;color:#fff;position:relative;overflow:hidden}.home-wrapper .banner .banner-conent[data-v-46a3a687]{max-width:1100px;margin:0 auto;position:relative;z-index:1;overflow:hidden}.home-wrapper .banner .banner-conent .hero[data-v-46a3a687]{text-align:center;margin-top:3rem}.home-wrapper .banner .banner-conent .hero img[data-v-46a3a687]{max-width:100%;max-height:240px;display:block;margin:2rem auto 1.5rem}.home-wrapper .banner .banner-conent .hero h1[data-v-46a3a687]{margin:0;font-size:3.2rem}.home-wrapper .banner .banner-conent .hero .action[data-v-46a3a687],.home-wrapper .banner .banner-conent .hero .description[data-v-46a3a687]{margin:1.5rem auto}.home-wrapper .banner .banner-conent .hero .description[data-v-46a3a687]{max-width:40rem;font-size:1.1rem;line-height:1.3;opacity:.9}.home-wrapper .banner .banner-conent .hero .action-button[data-v-46a3a687]{display:inline-block;font-size:1.2rem;background-color:#11a8cd;padding:.8rem 1.6rem;border-radius:4px;transition:background-color .1s ease;box-sizing:border-box;border-bottom:1px solid #0f97b9;color:#fff}.home-wrapper .banner .banner-conent .hero .action-button[data-v-46a3a687]:hover{background-color:#13bee8}.home-wrapper .banner .banner-conent .features[data-v-46a3a687]{padding:2rem 0;margin-top:2.5rem;display:flex;flex-wrap:wrap;align-items:flex-start;align-content:stretch;justify-content:space-between}.home-wrapper .banner .banner-conent .feature[data-v-46a3a687]{flex-grow:1;flex-basis:30%;max-width:30%;text-align:center}.home-wrapper .banner .banner-conent .feature a[data-v-46a3a687]{color:inherit}.home-wrapper .banner .banner-conent .feature a .feature-img[data-v-46a3a687]{width:10rem;height:10rem;animation:heart-46a3a687 1.2s ease-in-out 0s infinite alternate;animation-play-state:paused}.home-wrapper .banner .banner-conent .feature a h2[data-v-46a3a687]{font-weight:500;font-size:1.3rem;border-bottom:none;padding-bottom:0}.home-wrapper .banner .banner-conent .feature a p[data-v-46a3a687]{opacity:.8;padding:0 .8rem}.home-wrapper .banner .banner-conent .feature:hover .feature-img[data-v-46a3a687]{animation-play-state:running}.home-wrapper .banner .banner-conent .feature:hover h2[data-v-46a3a687],.home-wrapper .banner .banner-conent .feature:hover p[data-v-46a3a687]{color:#11a8cd}.home-wrapper .banner .slide-banner[data-v-46a3a687]{margin-top:2rem}.home-wrapper .banner .slide-banner .banner-wrapper[data-v-46a3a687]{position:relative}.home-wrapper .banner .slide-banner .slide-banner-scroll[data-v-46a3a687]{min-height:1px;overflow:hidden}.home-wrapper .banner .slide-banner .slide-banner-wrapper[data-v-46a3a687]{height:300px}.home-wrapper .banner .slide-banner .slide-banner-wrapper .slide-item[data-v-46a3a687]{display:inline-block;height:300px;width:100%;text-align:center}.home-wrapper .banner .slide-banner .slide-banner-wrapper .slide-item a[data-v-46a3a687]{color:inherit}.home-wrapper .banner .slide-banner .slide-banner-wrapper .slide-item a .feature-img[data-v-46a3a687]{width:10rem;height:10rem}.home-wrapper .banner .slide-banner .slide-banner-wrapper .slide-item a h2[data-v-46a3a687]{font-size:1.1rem;font-weight:500;border-bottom:none;padding-bottom:0}.home-wrapper .banner .slide-banner .slide-banner-wrapper .slide-item a p[data-v-46a3a687]{opacity:.8;padding:0 .8rem}.home-wrapper .banner .slide-banner .docs-wrapper[data-v-46a3a687]{position:absolute;bottom:25px;left:50%;transform:translateX(-50%)}.home-wrapper .banner .slide-banner .docs-wrapper .doc[data-v-46a3a687]{display:inline-block;margin:0 4px;width:8px;height:8px;border-radius:50%;background:var(--textColor);opacity:.9}.home-wrapper .banner .slide-banner .docs-wrapper .doc.active[data-v-46a3a687]{opacity:.5}.home-wrapper .banner.hide-banner[data-v-46a3a687]{display:none}.home-wrapper .banner.hide-banner+.main-wrapper[data-v-46a3a687]{margin-top:4.5rem}.home-wrapper .main-wrapper[data-v-46a3a687]{margin-top:2rem}.home-wrapper .main-wrapper .main-left .card-box[data-v-46a3a687]{margin-bottom:2rem}.home-wrapper .main-wrapper .main-left .pagination[data-v-46a3a687]{margin-bottom:3rem}.home-wrapper .main-wrapper .main-left .theme-vdoing-content[data-v-46a3a687]{padding:0 2rem;overflow:hidden;border:none}.home-wrapper .main-wrapper .main-left .theme-vdoing-content[data-v-46a3a687]>:first-child{padding-top:2rem}.home-wrapper .main-wrapper .main-left .theme-vdoing-content[data-v-46a3a687]>:last-child{padding-bottom:2rem}.home-wrapper .main-wrapper .main-right .custom-html-box[data-v-46a3a687]{padding:0;overflow:hidden}@media (max-width:1025px){.home-wrapper .banner .banner-conent .hero h1[data-v-46a3a687]{font-size:2.5rem}.home-wrapper .banner .banner-conent .hero .description[data-v-46a3a687]{font-size:1rem}.home-wrapper .banner .banner-conent .feature a h2[data-v-46a3a687]{font-size:1.1rem}.home-wrapper .banner .banner-conent .feature a .feature-img[data-v-46a3a687]{width:9rem;height:9rem}}@media (max-width:719px){.home-wrapper .banner .banner-conent .features[data-v-46a3a687]{display:none!important}}@media (max-width:419px){.home-wrapper .banner-conent[data-v-46a3a687]{padding-left:1.5rem;padding-right:1.5rem}.home-wrapper .banner-conent .hero img[data-v-46a3a687]{max-height:210px;margin:2rem auto 1.2rem}.home-wrapper .banner-conent .hero h1[data-v-46a3a687]{font-size:2rem}.home-wrapper .banner-conent .hero .action[data-v-46a3a687],.home-wrapper .banner-conent .hero .description[data-v-46a3a687],.home-wrapper .banner-conent .hero h1[data-v-46a3a687]{margin:1.2rem auto}.home-wrapper .banner-conent .hero .description[data-v-46a3a687]{font-size:1.2rem}.home-wrapper .banner-conent .hero .action-button[data-v-46a3a687]{font-size:1rem;padding:.6rem 1.2rem}.home-wrapper .banner-conent .feature h2[data-v-46a3a687]{font-size:1.25rem}}@media (max-width:719px){.theme-style-line .main-wrapper[data-v-46a3a687]{margin-top:-1px}}@keyframes heart-46a3a687{0%{transform:translate(0)}to{transform:translateY(8px)}}.search-box{display:inline-block;position:relative;margin-right:1rem}.search-box input{cursor:text;width:10rem;height:2rem;color:var(--textColor);display:inline-block;border:1px solid var(--borderColor,#ccc);border-radius:2rem;font-size:.9rem;line-height:2rem;padding:0 .5rem 0 2rem;outline:none;transition:width .2s ease;background:url(/assets/img/search.83621669.svg) .6rem .5rem no-repeat;background-size:1rem}.search-box input:focus{cursor:auto;border-color:#11a8cd}.search-box .suggestions{background:var(--mainBg,#fff);width:20rem;position:absolute;top:1.5rem;border:1px solid var(--borderColor,#ccc);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:var(--textColor);opacity:.75}.search-box .suggestion a .page-title{font-weight:600}.search-box .suggestion a .header{font-size:.9em;margin-left:.25em}.search-box .suggestion.focused,.search-box .suggestion:hover{background-color:hsla(0,0%,58.8%,.2)}.search-box .suggestion.focused a{color:#11a8cd}@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}@media (max-width:719px){.sidebar-button{display:block}}.sidebar-button .icon{display:block;width:1.25rem;height:1.25rem}@media (min-width:720px){.sidebar-button{width:40px;height:40px;display:inline-block;position:fixed;left:0;top:4.6rem;text-align:center;line-height:44px;margin:5px 8px;color:#888;border-radius:50%;padding:0;transition:all .2s}.sidebar-button:hover{background:#11a8cd;color:#fff;box-shadow:0 0 6px #11a8cd}.sidebar-button .icon{display:inline;width:1rem;height:1rem}}.dropdown-enter,.dropdown-leave-to{height:0!important}.dropdown-wrapper{cursor:pointer}.dropdown-wrapper .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:var(--textColor)}.dropdown-wrapper .dropdown-title:hover{border-color:transparent}.dropdown-wrapper .dropdown-title .arrow{vertical-align:middle;margin-top:-1px;margin-left:.4rem}.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 var(--borderColor);padding:.45rem 1.5rem 0 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:#11a8cd}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active:after{content:"";width:0;height:0;border-left:5px solid #11a8cd;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{font-weight:600;font-size:inherit}.dropdown-wrapper .dropdown-title:hover{color:#11a8cd}.dropdown-wrapper .dropdown-title .link-title{display:none}.dropdown-wrapper .dropdown-title .title{display:inline-block!important}.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 .dropdown-title .arrow{border-left:4px solid transparent;border-right:4px solid transparent;border-top:6px solid #ccc;border-bottom:0}.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:var(--mainBg);padding:.6rem 0;border-bottom-color:var(--borderColor);border:1px solid var(--borderColor);text-align:left;border-radius:.25rem;white-space:nowrap;margin:0}.nav-item .dropdown-title a.router-link-active,.nav-item .dropdown-title a:hover{margin-bottom:-2px;border-bottom:2px solid #13b9e2}}.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:#11a8cd}.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:959px){.nav-links .nav-item{margin-left:1.2rem}}@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:var(--textColor)}.nav-item>a:not(.external).router-link-active,.nav-item>a:not(.external):hover{margin-bottom:-2px;border-bottom:2px solid #13b9e2}}.navbar{padding:.7rem 1.5rem;line-height:2.2rem;transition:transform .3s}.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:var(--textColor);position:relative}.navbar .links{padding-left:1.5rem;box-sizing:border-box;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}.hide-navbar .navbar{transform:translateY(-100%)}@media (max-width:959px){.navbar .site-name{display:none}}@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:860px;padding-top:1rem;padding-bottom:1rem;overflow:auto}.page-edit .edit-link{display:inline-block;float:left;margin:0 2rem .5rem 0}.page-edit .edit-link a{margin-right:.25rem}.page-edit .tags{float:left}.page-edit .tags a{margin:0 .8rem .5rem 0;display:inline-block;color:var(--textLightenColor);padding:.2rem .7rem;font-size:.9em;background-color:hsla(0,0%,50.2%,.08);border-radius:3px;opacity:.8}.page-edit .last-updated{float:right;font-size:.9em}.page-edit .last-updated .prefix{font-weight:500;color:var(--textColor);opacity:.8}.page-edit .last-updated .time{font-weight:400;color:#aaa}@media (max-width:719px){.page-edit .edit-link,.page-edit .tags{margin-bottom:.5rem}.page-edit .last-updated{width:100%;font-size:.8em;text-align:left}}.page-nav{max-width:860px;padding-top:1rem;padding-bottom:0}.page-nav .inner{min-height:2rem;margin-top:0;border-top:1px solid var(--borderColor);padding-top:1rem;overflow:auto}.page-nav .next{float:right}.page-nav-centre-wrap .page-nav-centre{position:fixed;top:50%;width:80px;height:70px;margin-top:-35px;outline:0;transition:all .2s;border-radius:3px;opacity:.55;z-index:99}@media (max-width:1340px){.page-nav-centre-wrap .page-nav-centre{width:50px}}@media (max-width:960px){.page-nav-centre-wrap .page-nav-centre{display:none}}.page-nav-centre-wrap .page-nav-centre:hover{background:hsla(0,0%,60%,.15);opacity:1}.page-nav-centre-wrap .page-nav-centre:hover .tooltip{display:block}.page-nav-centre-wrap .page-nav-centre:before{content:"";display:block;width:10px;height:10px;border-top:2px solid #999;border-right:2px solid #999;position:absolute;top:0;right:0;bottom:0;left:0;margin:auto}.page-nav-centre-wrap .page-nav-centre .tooltip{display:none;background:rgba(0,0,0,.5);color:#fff;padding:4px 8px;font-size:13px;border-radius:3px;position:fixed;max-width:200px;z-index:99}.page-nav-centre-wrap .page-nav-centre-prev{left:0}.page-nav-centre-wrap .page-nav-centre-prev:before{transform:rotate(-135deg)}.page-nav-centre-wrap .page-nav-centre-next{right:0}.page-nav-centre-wrap .page-nav-centre-next:before{transform:rotate(45deg)}.sidebar-open .page-nav-centre-wrap .page-nav-centre-prev{left:18rem}.no-sidebar .page-nav-centre-wrap .page-nav-centre-prev{left:0}.theme-mode-light[data-v-06225672]{--bodyBg:#f4f4f4;--mainBg:#fff;--sidebarBg:hsla(0,0%,100%,0.8);--blurBg:hsla(0,0%,100%,0.9);--customBlockBg:#f1f1f1;--textColor:#00323c;--textLightenColor:#0085ad;--borderColor:rgba(0,0,0,0.12);--codeBg:#f6f6f6;--codeColor:#525252}.theme-mode-light code[class*=language-][data-v-06225672],.theme-mode-light pre[class*=language-][data-v-06225672]{color:#000;background:none;text-shadow:0 1px #fff;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;hyphens:none}.theme-mode-light code[class*=language-][data-v-06225672]::-moz-selection,.theme-mode-light code[class*=language-][data-v-06225672] ::-moz-selection,.theme-mode-light pre[class*=language-][data-v-06225672]::-moz-selection,.theme-mode-light pre[class*=language-][data-v-06225672] ::-moz-selection{text-shadow:none;background:#b3d4fc}.theme-mode-light code[class*=language-][data-v-06225672]::selection,.theme-mode-light code[class*=language-][data-v-06225672] ::selection,.theme-mode-light pre[class*=language-][data-v-06225672]::selection,.theme-mode-light pre[class*=language-][data-v-06225672] ::selection{text-shadow:none;background:#b3d4fc}@media print{.theme-mode-light code[class*=language-][data-v-06225672],.theme-mode-light pre[class*=language-][data-v-06225672]{text-shadow:none}}.theme-mode-light pre[class*=language-][data-v-06225672]{padding:1em;margin:.5em 0;overflow:auto}.theme-mode-light :not(pre)>code[class*=language-][data-v-06225672],.theme-mode-light pre[class*=language-][data-v-06225672]{background:#f5f2f0}.theme-mode-light :not(pre)>code[class*=language-][data-v-06225672]{padding:.1em;border-radius:.3em;white-space:normal}.theme-mode-light .token.cdata[data-v-06225672],.theme-mode-light .token.comment[data-v-06225672],.theme-mode-light .token.doctype[data-v-06225672],.theme-mode-light .token.prolog[data-v-06225672]{color:#708090}.theme-mode-light .token.punctuation[data-v-06225672]{color:#999}.theme-mode-light .namespace[data-v-06225672]{opacity:.7}.theme-mode-light .token.boolean[data-v-06225672],.theme-mode-light .token.constant[data-v-06225672],.theme-mode-light .token.deleted[data-v-06225672],.theme-mode-light .token.number[data-v-06225672],.theme-mode-light .token.property[data-v-06225672],.theme-mode-light .token.symbol[data-v-06225672],.theme-mode-light .token.tag[data-v-06225672]{color:#905}.theme-mode-light .token.attr-name[data-v-06225672],.theme-mode-light .token.builtin[data-v-06225672],.theme-mode-light .token.char[data-v-06225672],.theme-mode-light .token.inserted[data-v-06225672],.theme-mode-light .token.selector[data-v-06225672],.theme-mode-light .token.string[data-v-06225672]{color:#690}.theme-mode-light .language-css .token.string[data-v-06225672],.theme-mode-light .style .token.string[data-v-06225672],.theme-mode-light .token.entity[data-v-06225672],.theme-mode-light .token.operator[data-v-06225672],.theme-mode-light .token.url[data-v-06225672]{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.theme-mode-light .token.atrule[data-v-06225672],.theme-mode-light .token.attr-value[data-v-06225672],.theme-mode-light .token.keyword[data-v-06225672]{color:#07a}.theme-mode-light .token.class-name[data-v-06225672],.theme-mode-light .token.function[data-v-06225672]{color:#dd4a68}.theme-mode-light .token.important[data-v-06225672],.theme-mode-light .token.regex[data-v-06225672],.theme-mode-light .token.variable[data-v-06225672]{color:#e90}.theme-mode-light .token.bold[data-v-06225672],.theme-mode-light .token.important[data-v-06225672]{font-weight:700}.theme-mode-light .token.italic[data-v-06225672]{font-style:italic}.theme-mode-light .token.entity[data-v-06225672]{cursor:help}.theme-mode-light div[class*=language-] .highlight-lines .highlighted[data-v-06225672],.theme-mode-light div[class*=language-].line-numbers-mode .highlight-lines .highlighted[data-v-06225672]:before{background-color:hsla(0,0%,78.4%,.4)}.theme-mode-dark[data-v-06225672]{--bodyBg:#27272b;--mainBg:#1e1e22;--sidebarBg:rgba(30,30,34,0.8);--blurBg:rgba(30,30,34,0.8);--customBlockBg:#27272b;--textColor:#9b9baa;--textLightenColor:#0085ad;--borderColor:#30363d;--codeBg:#252526;--codeColor:#fff}.theme-mode-dark code[class*=language-][data-v-06225672],.theme-mode-dark pre[class*=language-][data-v-06225672]{color:#ccc;background:none;text-shadow: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;hyphens:none}.theme-mode-dark pre[class*=language-][data-v-06225672]{padding:1em;margin:.5em 0;overflow:auto}.theme-mode-dark :not(pre)>code[class*=language-][data-v-06225672],.theme-mode-dark pre[class*=language-][data-v-06225672]{background:#2d2d2d}.theme-mode-dark :not(pre)>code[class*=language-][data-v-06225672]{padding:.1em;border-radius:.3em;white-space:normal}.theme-mode-dark .token.block-comment[data-v-06225672],.theme-mode-dark .token.cdata[data-v-06225672],.theme-mode-dark .token.comment[data-v-06225672],.theme-mode-dark .token.doctype[data-v-06225672],.theme-mode-dark .token.prolog[data-v-06225672]{color:#999}.theme-mode-dark .token.punctuation[data-v-06225672]{color:#ccc}.theme-mode-dark .token.attr-name[data-v-06225672],.theme-mode-dark .token.deleted[data-v-06225672],.theme-mode-dark .token.namespace[data-v-06225672],.theme-mode-dark .token.tag[data-v-06225672]{color:#e2777a}.theme-mode-dark .token.function-name[data-v-06225672]{color:#6196cc}.theme-mode-dark .token.boolean[data-v-06225672],.theme-mode-dark .token.function[data-v-06225672],.theme-mode-dark .token.number[data-v-06225672]{color:#f08d49}.theme-mode-dark .token.class-name[data-v-06225672],.theme-mode-dark .token.constant[data-v-06225672],.theme-mode-dark .token.property[data-v-06225672],.theme-mode-dark .token.symbol[data-v-06225672]{color:#f8c555}.theme-mode-dark .token.atrule[data-v-06225672],.theme-mode-dark .token.builtin[data-v-06225672],.theme-mode-dark .token.important[data-v-06225672],.theme-mode-dark .token.keyword[data-v-06225672],.theme-mode-dark .token.selector[data-v-06225672]{color:#cc99cd}.theme-mode-dark .token.attr-value[data-v-06225672],.theme-mode-dark .token.char[data-v-06225672],.theme-mode-dark .token.regex[data-v-06225672],.theme-mode-dark .token.string[data-v-06225672],.theme-mode-dark .token.variable[data-v-06225672]{color:#7ec699}.theme-mode-dark .token.entity[data-v-06225672],.theme-mode-dark .token.operator[data-v-06225672],.theme-mode-dark .token.url[data-v-06225672]{color:#67cdcc}.theme-mode-dark .language-css .token.string[data-v-06225672],.theme-mode-dark .style .token.string[data-v-06225672],.theme-mode-dark .token.entity[data-v-06225672],.theme-mode-dark .token.operator[data-v-06225672],.theme-mode-dark .token.url[data-v-06225672]{background:none}.theme-mode-dark .token.bold[data-v-06225672],.theme-mode-dark .token.important[data-v-06225672]{font-weight:700}.theme-mode-dark .token.italic[data-v-06225672]{font-style:italic}.theme-mode-dark .token.entity[data-v-06225672]{cursor:help}.theme-mode-dark .token.inserted[data-v-06225672]{color:green}.theme-mode-read[data-v-06225672]{--bodyBg:#ececcc;--mainBg:#f5f5d5;--sidebarBg:rgba(245,245,213,0.8);--blurBg:rgba(245,245,213,0.9);--customBlockBg:#ececcc;--textColor:#704214;--textLightenColor:#963;--borderColor:rgba(0,0,0,0.15);--codeBg:#282c34;--codeColor:#fff}.theme-mode-read code[class*=language-][data-v-06225672],.theme-mode-read pre[class*=language-][data-v-06225672]{color:#ccc;background:none;text-shadow: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;hyphens:none}.theme-mode-read pre[class*=language-][data-v-06225672]{padding:1em;margin:.5em 0;overflow:auto}.theme-mode-read :not(pre)>code[class*=language-][data-v-06225672],.theme-mode-read pre[class*=language-][data-v-06225672]{background:#2d2d2d}.theme-mode-read :not(pre)>code[class*=language-][data-v-06225672]{padding:.1em;border-radius:.3em;white-space:normal}.theme-mode-read .token.block-comment[data-v-06225672],.theme-mode-read .token.cdata[data-v-06225672],.theme-mode-read .token.comment[data-v-06225672],.theme-mode-read .token.doctype[data-v-06225672],.theme-mode-read .token.prolog[data-v-06225672]{color:#999}.theme-mode-read .token.punctuation[data-v-06225672]{color:#ccc}.theme-mode-read .token.attr-name[data-v-06225672],.theme-mode-read .token.deleted[data-v-06225672],.theme-mode-read .token.namespace[data-v-06225672],.theme-mode-read .token.tag[data-v-06225672]{color:#e2777a}.theme-mode-read .token.function-name[data-v-06225672]{color:#6196cc}.theme-mode-read .token.boolean[data-v-06225672],.theme-mode-read .token.function[data-v-06225672],.theme-mode-read .token.number[data-v-06225672]{color:#f08d49}.theme-mode-read .token.class-name[data-v-06225672],.theme-mode-read .token.constant[data-v-06225672],.theme-mode-read .token.property[data-v-06225672],.theme-mode-read .token.symbol[data-v-06225672]{color:#f8c555}.theme-mode-read .token.atrule[data-v-06225672],.theme-mode-read .token.builtin[data-v-06225672],.theme-mode-read .token.important[data-v-06225672],.theme-mode-read .token.keyword[data-v-06225672],.theme-mode-read .token.selector[data-v-06225672]{color:#cc99cd}.theme-mode-read .token.attr-value[data-v-06225672],.theme-mode-read .token.char[data-v-06225672],.theme-mode-read .token.regex[data-v-06225672],.theme-mode-read .token.string[data-v-06225672],.theme-mode-read .token.variable[data-v-06225672]{color:#7ec699}.theme-mode-read .token.entity[data-v-06225672],.theme-mode-read .token.operator[data-v-06225672],.theme-mode-read .token.url[data-v-06225672]{color:#67cdcc}.theme-mode-read .language-css .token.string[data-v-06225672],.theme-mode-read .style .token.string[data-v-06225672],.theme-mode-read .token.entity[data-v-06225672],.theme-mode-read .token.operator[data-v-06225672],.theme-mode-read .token.url[data-v-06225672]{background:none}.theme-mode-read .token.bold[data-v-06225672],.theme-mode-read .token.important[data-v-06225672]{font-weight:700}.theme-mode-read .token.italic[data-v-06225672]{font-style:italic}.theme-mode-read .token.entity[data-v-06225672]{cursor:help}.theme-mode-read .token.inserted[data-v-06225672]{color:green}.theme-style-line.theme-mode-light[data-v-06225672]{--bodyBg:#fff}.theme-style-line.theme-mode-dark[data-v-06225672]{--bodyBg:#1e1e22}.theme-style-line.theme-mode-read[data-v-06225672]{--bodyBg:#f5f5d5}.articleInfo-wrap[data-v-06225672]{max-width:860px}.theme-style-line .articleInfo-wrap .articleInfo[data-v-06225672]{padding-top:.5rem}.articleInfo-wrap[data-v-06225672]{position:relative;z-index:1;color:#888}.articleInfo-wrap .articleInfo[data-v-06225672]{overflow:hidden;font-size:.92rem}.articleInfo-wrap .articleInfo .breadcrumbs[data-v-06225672]{margin:0;padding:0;overflow:hidden;display:inline-block;line-height:2rem}@media (max-width:960px){.articleInfo-wrap .articleInfo .breadcrumbs[data-v-06225672]{width:100%}}.articleInfo-wrap .articleInfo .breadcrumbs li[data-v-06225672]{list-style-type:none;float:left;padding-right:5px}.articleInfo-wrap .articleInfo .breadcrumbs li[data-v-06225672]:after{content:"/";margin-left:5px;color:#999}.articleInfo-wrap .articleInfo .breadcrumbs li[data-v-06225672]:last-child:after{content:""}.articleInfo-wrap .articleInfo .breadcrumbs li a[data-v-06225672]{color:#888}.articleInfo-wrap .articleInfo .breadcrumbs li a[data-v-06225672]:before{font-size:.92rem}.articleInfo-wrap .articleInfo .breadcrumbs li a[data-v-06225672]:hover{color:#11a8cd}.articleInfo-wrap .articleInfo .breadcrumbs li .icon-home[data-v-06225672]{text-decoration:none}.articleInfo-wrap .articleInfo .info[data-v-06225672]{float:right;line-height:32px}@media (max-width:960px){.articleInfo-wrap .articleInfo .info[data-v-06225672]{float:left}}.articleInfo-wrap .articleInfo .info div[data-v-06225672]{float:left;margin-left:20px;font-size:.8rem}@media (max-width:960px){.articleInfo-wrap .articleInfo .info div[data-v-06225672]{margin:0 20px 0 0}}.articleInfo-wrap .articleInfo .info div[data-v-06225672]:before{margin-right:3px}.articleInfo-wrap .articleInfo .info div a[data-v-06225672]{color:#888}.articleInfo-wrap .articleInfo .info div a[data-v-06225672]:hover{text-decoration:none}.articleInfo-wrap .articleInfo .info div a.beLink[data-v-06225672]:hover{color:#11a8cd;text-decoration:underline}.theme-mode-light[data-v-644d539c]{--bodyBg:#f4f4f4;--mainBg:#fff;--sidebarBg:hsla(0,0%,100%,0.8);--blurBg:hsla(0,0%,100%,0.9);--customBlockBg:#f1f1f1;--textColor:#00323c;--textLightenColor:#0085ad;--borderColor:rgba(0,0,0,0.12);--codeBg:#f6f6f6;--codeColor:#525252}.theme-mode-light code[class*=language-][data-v-644d539c],.theme-mode-light pre[class*=language-][data-v-644d539c]{color:#000;background:none;text-shadow:0 1px #fff;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;hyphens:none}.theme-mode-light code[class*=language-][data-v-644d539c]::-moz-selection,.theme-mode-light code[class*=language-][data-v-644d539c] ::-moz-selection,.theme-mode-light pre[class*=language-][data-v-644d539c]::-moz-selection,.theme-mode-light pre[class*=language-][data-v-644d539c] ::-moz-selection{text-shadow:none;background:#b3d4fc}.theme-mode-light code[class*=language-][data-v-644d539c]::selection,.theme-mode-light code[class*=language-][data-v-644d539c] ::selection,.theme-mode-light pre[class*=language-][data-v-644d539c]::selection,.theme-mode-light pre[class*=language-][data-v-644d539c] ::selection{text-shadow:none;background:#b3d4fc}@media print{.theme-mode-light code[class*=language-][data-v-644d539c],.theme-mode-light pre[class*=language-][data-v-644d539c]{text-shadow:none}}.theme-mode-light pre[class*=language-][data-v-644d539c]{padding:1em;margin:.5em 0;overflow:auto}.theme-mode-light :not(pre)>code[class*=language-][data-v-644d539c],.theme-mode-light pre[class*=language-][data-v-644d539c]{background:#f5f2f0}.theme-mode-light :not(pre)>code[class*=language-][data-v-644d539c]{padding:.1em;border-radius:.3em;white-space:normal}.theme-mode-light .token.cdata[data-v-644d539c],.theme-mode-light .token.comment[data-v-644d539c],.theme-mode-light .token.doctype[data-v-644d539c],.theme-mode-light .token.prolog[data-v-644d539c]{color:#708090}.theme-mode-light .token.punctuation[data-v-644d539c]{color:#999}.theme-mode-light .namespace[data-v-644d539c]{opacity:.7}.theme-mode-light .token.boolean[data-v-644d539c],.theme-mode-light .token.constant[data-v-644d539c],.theme-mode-light .token.deleted[data-v-644d539c],.theme-mode-light .token.number[data-v-644d539c],.theme-mode-light .token.property[data-v-644d539c],.theme-mode-light .token.symbol[data-v-644d539c],.theme-mode-light .token.tag[data-v-644d539c]{color:#905}.theme-mode-light .token.attr-name[data-v-644d539c],.theme-mode-light .token.builtin[data-v-644d539c],.theme-mode-light .token.char[data-v-644d539c],.theme-mode-light .token.inserted[data-v-644d539c],.theme-mode-light .token.selector[data-v-644d539c],.theme-mode-light .token.string[data-v-644d539c]{color:#690}.theme-mode-light .language-css .token.string[data-v-644d539c],.theme-mode-light .style .token.string[data-v-644d539c],.theme-mode-light .token.entity[data-v-644d539c],.theme-mode-light .token.operator[data-v-644d539c],.theme-mode-light .token.url[data-v-644d539c]{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.theme-mode-light .token.atrule[data-v-644d539c],.theme-mode-light .token.attr-value[data-v-644d539c],.theme-mode-light .token.keyword[data-v-644d539c]{color:#07a}.theme-mode-light .token.class-name[data-v-644d539c],.theme-mode-light .token.function[data-v-644d539c]{color:#dd4a68}.theme-mode-light .token.important[data-v-644d539c],.theme-mode-light .token.regex[data-v-644d539c],.theme-mode-light .token.variable[data-v-644d539c]{color:#e90}.theme-mode-light .token.bold[data-v-644d539c],.theme-mode-light .token.important[data-v-644d539c]{font-weight:700}.theme-mode-light .token.italic[data-v-644d539c]{font-style:italic}.theme-mode-light .token.entity[data-v-644d539c]{cursor:help}.theme-mode-light div[class*=language-] .highlight-lines .highlighted[data-v-644d539c],.theme-mode-light div[class*=language-].line-numbers-mode .highlight-lines .highlighted[data-v-644d539c]:before{background-color:hsla(0,0%,78.4%,.4)}.theme-mode-dark[data-v-644d539c]{--bodyBg:#27272b;--mainBg:#1e1e22;--sidebarBg:rgba(30,30,34,0.8);--blurBg:rgba(30,30,34,0.8);--customBlockBg:#27272b;--textColor:#9b9baa;--textLightenColor:#0085ad;--borderColor:#30363d;--codeBg:#252526;--codeColor:#fff}.theme-mode-dark code[class*=language-][data-v-644d539c],.theme-mode-dark pre[class*=language-][data-v-644d539c]{color:#ccc;background:none;text-shadow: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;hyphens:none}.theme-mode-dark pre[class*=language-][data-v-644d539c]{padding:1em;margin:.5em 0;overflow:auto}.theme-mode-dark :not(pre)>code[class*=language-][data-v-644d539c],.theme-mode-dark pre[class*=language-][data-v-644d539c]{background:#2d2d2d}.theme-mode-dark :not(pre)>code[class*=language-][data-v-644d539c]{padding:.1em;border-radius:.3em;white-space:normal}.theme-mode-dark .token.block-comment[data-v-644d539c],.theme-mode-dark .token.cdata[data-v-644d539c],.theme-mode-dark .token.comment[data-v-644d539c],.theme-mode-dark .token.doctype[data-v-644d539c],.theme-mode-dark .token.prolog[data-v-644d539c]{color:#999}.theme-mode-dark .token.punctuation[data-v-644d539c]{color:#ccc}.theme-mode-dark .token.attr-name[data-v-644d539c],.theme-mode-dark .token.deleted[data-v-644d539c],.theme-mode-dark .token.namespace[data-v-644d539c],.theme-mode-dark .token.tag[data-v-644d539c]{color:#e2777a}.theme-mode-dark .token.function-name[data-v-644d539c]{color:#6196cc}.theme-mode-dark .token.boolean[data-v-644d539c],.theme-mode-dark .token.function[data-v-644d539c],.theme-mode-dark .token.number[data-v-644d539c]{color:#f08d49}.theme-mode-dark .token.class-name[data-v-644d539c],.theme-mode-dark .token.constant[data-v-644d539c],.theme-mode-dark .token.property[data-v-644d539c],.theme-mode-dark .token.symbol[data-v-644d539c]{color:#f8c555}.theme-mode-dark .token.atrule[data-v-644d539c],.theme-mode-dark .token.builtin[data-v-644d539c],.theme-mode-dark .token.important[data-v-644d539c],.theme-mode-dark .token.keyword[data-v-644d539c],.theme-mode-dark .token.selector[data-v-644d539c]{color:#cc99cd}.theme-mode-dark .token.attr-value[data-v-644d539c],.theme-mode-dark .token.char[data-v-644d539c],.theme-mode-dark .token.regex[data-v-644d539c],.theme-mode-dark .token.string[data-v-644d539c],.theme-mode-dark .token.variable[data-v-644d539c]{color:#7ec699}.theme-mode-dark .token.entity[data-v-644d539c],.theme-mode-dark .token.operator[data-v-644d539c],.theme-mode-dark .token.url[data-v-644d539c]{color:#67cdcc}.theme-mode-dark .language-css .token.string[data-v-644d539c],.theme-mode-dark .style .token.string[data-v-644d539c],.theme-mode-dark .token.entity[data-v-644d539c],.theme-mode-dark .token.operator[data-v-644d539c],.theme-mode-dark .token.url[data-v-644d539c]{background:none}.theme-mode-dark .token.bold[data-v-644d539c],.theme-mode-dark .token.important[data-v-644d539c]{font-weight:700}.theme-mode-dark .token.italic[data-v-644d539c]{font-style:italic}.theme-mode-dark .token.entity[data-v-644d539c]{cursor:help}.theme-mode-dark .token.inserted[data-v-644d539c]{color:green}.theme-mode-read[data-v-644d539c]{--bodyBg:#ececcc;--mainBg:#f5f5d5;--sidebarBg:rgba(245,245,213,0.8);--blurBg:rgba(245,245,213,0.9);--customBlockBg:#ececcc;--textColor:#704214;--textLightenColor:#963;--borderColor:rgba(0,0,0,0.15);--codeBg:#282c34;--codeColor:#fff}.theme-mode-read code[class*=language-][data-v-644d539c],.theme-mode-read pre[class*=language-][data-v-644d539c]{color:#ccc;background:none;text-shadow: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;hyphens:none}.theme-mode-read pre[class*=language-][data-v-644d539c]{padding:1em;margin:.5em 0;overflow:auto}.theme-mode-read :not(pre)>code[class*=language-][data-v-644d539c],.theme-mode-read pre[class*=language-][data-v-644d539c]{background:#2d2d2d}.theme-mode-read :not(pre)>code[class*=language-][data-v-644d539c]{padding:.1em;border-radius:.3em;white-space:normal}.theme-mode-read .token.block-comment[data-v-644d539c],.theme-mode-read .token.cdata[data-v-644d539c],.theme-mode-read .token.comment[data-v-644d539c],.theme-mode-read .token.doctype[data-v-644d539c],.theme-mode-read .token.prolog[data-v-644d539c]{color:#999}.theme-mode-read .token.punctuation[data-v-644d539c]{color:#ccc}.theme-mode-read .token.attr-name[data-v-644d539c],.theme-mode-read .token.deleted[data-v-644d539c],.theme-mode-read .token.namespace[data-v-644d539c],.theme-mode-read .token.tag[data-v-644d539c]{color:#e2777a}.theme-mode-read .token.function-name[data-v-644d539c]{color:#6196cc}.theme-mode-read .token.boolean[data-v-644d539c],.theme-mode-read .token.function[data-v-644d539c],.theme-mode-read .token.number[data-v-644d539c]{color:#f08d49}.theme-mode-read .token.class-name[data-v-644d539c],.theme-mode-read .token.constant[data-v-644d539c],.theme-mode-read .token.property[data-v-644d539c],.theme-mode-read .token.symbol[data-v-644d539c]{color:#f8c555}.theme-mode-read .token.atrule[data-v-644d539c],.theme-mode-read .token.builtin[data-v-644d539c],.theme-mode-read .token.important[data-v-644d539c],.theme-mode-read .token.keyword[data-v-644d539c],.theme-mode-read .token.selector[data-v-644d539c]{color:#cc99cd}.theme-mode-read .token.attr-value[data-v-644d539c],.theme-mode-read .token.char[data-v-644d539c],.theme-mode-read .token.regex[data-v-644d539c],.theme-mode-read .token.string[data-v-644d539c],.theme-mode-read .token.variable[data-v-644d539c]{color:#7ec699}.theme-mode-read .token.entity[data-v-644d539c],.theme-mode-read .token.operator[data-v-644d539c],.theme-mode-read .token.url[data-v-644d539c]{color:#67cdcc}.theme-mode-read .language-css .token.string[data-v-644d539c],.theme-mode-read .style .token.string[data-v-644d539c],.theme-mode-read .token.entity[data-v-644d539c],.theme-mode-read .token.operator[data-v-644d539c],.theme-mode-read .token.url[data-v-644d539c]{background:none}.theme-mode-read .token.bold[data-v-644d539c],.theme-mode-read .token.important[data-v-644d539c]{font-weight:700}.theme-mode-read .token.italic[data-v-644d539c]{font-style:italic}.theme-mode-read .token.entity[data-v-644d539c]{cursor:help}.theme-mode-read .token.inserted[data-v-644d539c]{color:green}.theme-style-line.theme-mode-light[data-v-644d539c]{--bodyBg:#fff}.theme-style-line.theme-mode-dark[data-v-644d539c]{--bodyBg:#1e1e22}.theme-style-line.theme-mode-read[data-v-644d539c]{--bodyBg:#f5f5d5}.theme-vdoing-content[data-v-644d539c]{margin-bottom:3.6rem}.title-tag[data-v-644d539c]{border:1px solid #ff5722;color:#ff5722;font-size:.8rem;padding:0 .35rem;border-radius:.2rem;margin-left:0;transform:translateY(-.05rem);display:inline-block}dd[data-v-644d539c],dl[data-v-644d539c]{margin:0}.column-wrapper[data-v-644d539c]{margin-top:1rem;display:flex;padding-bottom:2rem;border-bottom:1px solid var(--borderColor)}.column-wrapper img[data-v-644d539c]{width:80px;height:80px;border-radius:2px;margin-right:1rem}.column-wrapper .column-info .title[data-v-644d539c]{font-size:1.6rem}.column-wrapper .column-info .description[data-v-644d539c]{color:var(--textColor);opacity:.8;margin:.5rem 0}.catalogue-wrapper .catalogue-title[data-v-644d539c]{font-size:1.45rem;margin:2rem 0}.catalogue-wrapper .catalogue-content dl[data-v-644d539c]{margin-bottom:1.8rem}.catalogue-wrapper .catalogue-content dl.inline[data-v-644d539c]{display:inline-block;width:50%;margin-bottom:1rem}@media (max-width:419px){.catalogue-wrapper .catalogue-content dl.inline[data-v-644d539c]{width:100%}}.catalogue-wrapper .catalogue-content dl.inline a[data-v-644d539c]{width:100%}.catalogue-wrapper .catalogue-content dl:not(.inline) dt[data-v-644d539c]{margin-top:-3.6rem;padding-top:3.6rem}.catalogue-wrapper .catalogue-content dl dt[data-v-644d539c]{font-size:1.1rem}.catalogue-wrapper .catalogue-content dl dt:hover .header-anchor[data-v-644d539c]{opacity:1}.catalogue-wrapper .catalogue-content dl dd[data-v-644d539c]{margin-top:.7rem;margin-left:1rem}.catalogue-wrapper .catalogue-content dl a[data-v-644d539c]:not(.header-anchor){margin-bottom:.5rem;display:inline-block;width:50%}.catalogue-wrapper .catalogue-content dl a[data-v-644d539c]:not(.header-anchor):hover{color:#ff5722;text-decoration:none}@media (max-width:419px){.catalogue-wrapper .catalogue-content dl a[data-v-644d539c]:not(.header-anchor){width:100%}}.catalogue-wrapper .catalogue-content dl .sub-cat-wrap[data-v-644d539c]{margin:5px 0 8px;font-size:.95rem}.catalogue-wrapper .catalogue-content dl .sub-cat-wrap>a[data-v-644d539c]{padding-left:1rem;box-sizing:border-box}.catalogue-wrapper .catalogue-content dl .sub-cat-wrap .sub-title[data-v-644d539c]{margin-top:-3.6rem;padding-top:3.6rem;margin-bottom:6px;font-size:1rem}.catalogue-wrapper .catalogue-content dl .sub-cat-wrap:hover .header-anchor[data-v-644d539c]{opacity:1}.theme-style-line .right-menu-wrapper .right-menu-margin{border-left:1px solid var(--borderColor)}.right-menu-wrapper{width:230px;float:right;margin-right:-285px;position:sticky;top:0;font-size:.8rem}.right-menu-wrapper .right-menu-margin{margin-top:4.6rem;border-radius:3px;overflow:hidden}.right-menu-wrapper .right-menu-title{padding:10px 15px 0;background:var(--mainBg);font-size:1rem}.right-menu-wrapper .right-menu-title:after{content:"";display:block;width:100%;height:1px;background:var(--borderColor);margin-top:10px}.right-menu-wrapper .right-menu-content{max-height:80vh;position:relative;overflow:hidden;background:var(--mainBg);padding:4px 3px 4px 0}.right-menu-wrapper .right-menu-content::-webkit-scrollbar{width:3px;height:3px}.right-menu-wrapper .right-menu-content::-webkit-scrollbar-track-piece{background:none}.right-menu-wrapper .right-menu-content::-webkit-scrollbar-thumb:vertical{background-color:hsla(0,0%,49%,.3)}.right-menu-wrapper .right-menu-content:hover{overflow-y:auto;padding-right:0}.right-menu-wrapper .right-menu-content .right-menu-item{padding:4px 15px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;position:relative}.right-menu-wrapper .right-menu-content .right-menu-item.level2{font-size:.8rem}.right-menu-wrapper .right-menu-content .right-menu-item.level3{padding-left:27px}.right-menu-wrapper .right-menu-content .right-menu-item.level4{padding-left:37px}.right-menu-wrapper .right-menu-content .right-menu-item.level5{padding-left:47px}.right-menu-wrapper .right-menu-content .right-menu-item.level6{padding-left:57px}.right-menu-wrapper .right-menu-content .right-menu-item.active:before{content:"";position:absolute;top:5px;left:0;width:3px;height:14px;background:#11a8cd;border-radius:0 4px 4px 0}.right-menu-wrapper .right-menu-content .right-menu-item.active a{color:#11a8cd;opacity:1}.right-menu-wrapper .right-menu-content .right-menu-item a{color:var(--textColor);opacity:.75;display:inline-block;width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.right-menu-wrapper .right-menu-content .right-menu-item a:hover{opacity:1}.right-menu-wrapper .right-menu-content:hover{color:#11a8cd}.page>*{max-width:860px;margin:0 auto;padding:1rem 2.5rem 2rem}.page>:not(.footer){background:var(--mainBg);box-shadow:0 1px 2px 0 rgba(0,0,0,.1);margin-bottom:1rem}@media (min-width:940px){.page>:not(.footer){border-radius:2px}}@media (max-width:959px){.page>*{padding:1rem 2rem}}@media (max-width:419px){.page>*{padding:1rem 1.5rem}}.page{padding-bottom:2rem;display:block}@media (max-width:719px){.page{padding-top:3.6rem}}@media (min-width:719px){.page{padding-top:5.1rem}}@media (min-width:719px){.theme-style-line .page{padding-top:3.6rem}}.theme-style-line .page>:not(.footer){box-shadow:0 0}@media (min-width:720px){.theme-style-line .page .placeholder{height:1.2rem}}.theme-vdoing-wrapper .content-wrapper{position:relative}.theme-vdoing-wrapper h1 .title-tag{height:1.5rem;line-height:1.5rem;border:1px solid #ff5722;color:#ff5722;font-size:1rem;padding:0 .4rem;border-radius:.2rem;margin-left:.5rem;transform:translateY(-.25rem);display:inline-block}.theme-vdoing-wrapper h1 img{margin-bottom:-.2rem;margin-right:.2rem;max-width:2.2rem;max-height:2.2rem}.theme-vdoing-wrapper{--linesColor:rgba(50,0,0,0.05)}.theme-vdoing-wrapper.bg-style-1{background-image:linear-gradient(90deg,var(--linesColor) 3%,transparent 0),linear-gradient(0deg,var(--linesColor) 3%,transparent 0);background-position:50%;background-size:20px 20px}.theme-vdoing-wrapper.bg-style-2{background-image:repeating-linear-gradient(0,var(--linesColor),var(--linesColor) 1px,transparent 0,transparent 50%);background-size:30px 30px}.theme-vdoing-wrapper.bg-style-3{background-image:repeating-linear-gradient(90deg,var(--linesColor),var(--linesColor) 1px,transparent 0,transparent 50%);background-size:30px 30px}.theme-vdoing-wrapper.bg-style-4{background-image:repeating-linear-gradient(-45deg,var(--linesColor),var(--linesColor) 1px,transparent 0,transparent 50%);background-size:20px 20px}.theme-vdoing-wrapper.bg-style-5{background-image:repeating-linear-gradient(45deg,var(--linesColor),var(--linesColor) 1px,transparent 0,transparent 50%);background-size:20px 20px}.theme-vdoing-wrapper.bg-style-6{background-image:radial-gradient(var(--linesColor) 1px,transparent 0);background-size:10px 10px}.theme-mode-dark .theme-vdoing-wrapper{--linesColor:hsla(0,0%,49%,0.05)}@media (min-width:720px) and (max-width:1279px){.have-rightmenu .page{padding-right:.8rem!important}}@media (max-width:1279px){.have-rightmenu .right-menu-wrapper{display:none}}@media (min-width:1280px){.have-rightmenu .sidebar .sidebar-sub-headers{display:none}}.theme-container.only-sidebarItem:not(.have-rightmenu) .sidebar,.theme-container.only-sidebarItem:not(.have-rightmenu) .sidebar-button{display:none}@media (min-width:720px){.theme-container.only-sidebarItem:not(.have-rightmenu) .page{padding-left:.8rem!important}}@media (max-width:719px){.theme-container.only-sidebarItem:not(.have-rightmenu) .page{padding-left:0!important}.theme-container.only-sidebarItem:not(.have-rightmenu) .sidebar,.theme-container.only-sidebarItem:not(.have-rightmenu) .sidebar-button{display:block}}@media (min-width:720px) and (max-width:1279px){.theme-container.only-sidebarItem.have-rightmenu .sidebar,.theme-container.only-sidebarItem.have-rightmenu .sidebar-button{display:block}}@media (min-width:1280px){.theme-container.only-sidebarItem.have-rightmenu .sidebar,.theme-container.only-sidebarItem.have-rightmenu .sidebar-button{display:none}}.categories-page .categories-wrapper{position:sticky;top:4.5rem;max-height:calc(100vh - 10rem);min-height:4.2rem}@media (max-width:719px){.categories-page .categories-wrapper{display:none}}.categories-page .categories-wrapper .categories{padding-right:.5rem;max-height:calc(100vh - 14rem);min-height:2.2rem;overflow-y:auto;transition:all .2s;position:relative}.categories-page .categories-wrapper .categories::-webkit-scrollbar-track-piece{background-color:rgba(0,0,0,.05)}.categories-page .categories-wrapper .categories::-webkit-scrollbar-thumb:vertical{background-color:rgba(0,0,0,.15)}.categories-page .categories-wrapper .categories:hover::-webkit-scrollbar-track-piece{background-color:rgba(0,0,0,.1)}.categories-page .categories-wrapper .categories:hover::-webkit-scrollbar-thumb:vertical{background-color:rgba(0,0,0,.25)}.categories-page .main-left .categories-wrapper{position:relative;top:0;padding:.9rem 1.5rem;margin-bottom:.9rem;max-height:15rem;border-radius:0;display:none}@media (max-width:719px){.categories-page .main-left .categories-wrapper{display:block}}.categories-page .main-left .categories-wrapper .categories{max-height:12.3rem}@media (max-width:719px){.theme-style-line .categories-page .main-left .categories-wrapper{margin-top:-.91rem;margin-bottom:-1px;padding:.9rem .2rem .5rem}}.tags-page .tags-wrapper{position:sticky;top:4.5rem;max-height:calc(100vh - 10rem);min-height:4.2rem}@media (max-width:719px){.tags-page .tags-wrapper{display:none}}.tags-page .tags-wrapper .tags{max-height:calc(100vh - 14rem);min-height:2.2rem;overflow-x:hidden;overflow-y:auto;transition:all .2s}.tags-page .tags-wrapper .tags::-webkit-scrollbar-track-piece{background-color:rgba(0,0,0,.05)}.tags-page .tags-wrapper .tags::-webkit-scrollbar-thumb:vertical{background-color:rgba(0,0,0,.15)}.tags-page .tags-wrapper .tags:hover::-webkit-scrollbar-track-piece{background-color:rgba(0,0,0,.1)}.tags-page .tags-wrapper .tags:hover::-webkit-scrollbar-thumb:vertical{background-color:rgba(0,0,0,.25)}.tags-page .main-left .tags-wrapper{position:relative;top:0;padding:.9rem 1.5rem;margin-bottom:.9rem;max-height:15rem;border-radius:0;display:none}@media (max-width:719px){.tags-page .main-left .tags-wrapper{display:block}}.tags-page .main-left .tags-wrapper .tags{max-height:11.5rem}@media (max-width:719px){.theme-style-line .tags-page .main-left .tags-wrapper{margin-top:-.91rem;margin-bottom:-1px}}.archives-page .theme-vdoing-wrapper{max-width:860px;margin:0 auto;padding:1rem 2.5rem 2rem}.archives-page .theme-vdoing-wrapper:not(.footer){background:var(--mainBg);box-shadow:0 1px 2px 0 rgba(0,0,0,.1);margin-bottom:1rem}@media (min-width:940px){.archives-page .theme-vdoing-wrapper:not(.footer){border-radius:2px}}@media (max-width:959px){.archives-page .theme-vdoing-wrapper{padding:1rem 2rem}}@media (max-width:419px){.archives-page .theme-vdoing-wrapper{padding:1rem 1.5rem}}.theme-style-line .archives-page .theme-vdoing-wrapper{box-shadow:0 0}.archives-page .theme-vdoing-wrapper{position:relative}@media (min-width:940px){.archives-page .theme-vdoing-wrapper{margin-top:1.5rem!important}}.archives-page .theme-vdoing-wrapper .count{text-align:right;margin-top:-2.5rem;font-size:.85rem;opacity:.8}.archives-page .theme-vdoing-wrapper li,.archives-page .theme-vdoing-wrapper ul{margin:0;padding:0}.archives-page .theme-vdoing-wrapper ul{margin-top:2rem}.archives-page .theme-vdoing-wrapper li{list-style:none}.archives-page .theme-vdoing-wrapper li.year{position:sticky;top:3.6rem;background:var(--mainBg);z-index:1}.archives-page .theme-vdoing-wrapper li.year:not(:first-child){margin-top:3.5rem}.archives-page .theme-vdoing-wrapper li h2{margin-bottom:.8rem;font-weight:400;padding:.5rem 0}.archives-page .theme-vdoing-wrapper li h2 span{font-size:.85rem;font-weight:300;float:right;margin-top:1rem}.archives-page .theme-vdoing-wrapper li a{display:block;color:var(--textColor);transition:padding .3s;padding:.5rem 2rem;line-height:1.2rem}.archives-page .theme-vdoing-wrapper li a:hover{padding-left:2.5rem;color:#11a8cd;background:#f9f9f9}@media (max-width:940px){.archives-page .theme-vdoing-wrapper li a{padding:.5rem 1rem;font-weight:400}.archives-page .theme-vdoing-wrapper li a:hover{padding-left:1.5rem}}.archives-page .theme-vdoing-wrapper li a span.date{opacity:.6;font-size:.85rem;font-weight:400;margin-right:.3rem}.archives-page .theme-vdoing-wrapper li a .title-tag{border:1px solid #ff5722;color:#ff5722;font-size:.8rem;padding:0 .35rem;border-radius:.2rem;margin-left:0;transform:translateY(-.05rem);display:inline-block}.archives-page .theme-vdoing-wrapper .loadmore{text-align:center;margin-top:1rem;opacity:.5}.theme-mode-dark .archives-page .theme-vdoing-wrapper li a:hover,.theme-mode-read .archives-page .theme-vdoing-wrapper li a:hover{background:var(--customBlockBg)}.hide-navbar .archives-page .theme-vdoing-wrapper li.year{top:0}.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:1.01em;line-height:1.4;font-weight:700;padding-left:2rem}.sidebar-group.is-sub-group>.sidebar-group-items{padding-left:1rem}.sidebar-group.is-sub-group>.sidebar-group-items>li>.sidebar-link{font-size:.98em;border-left:none}.sidebar-group.depth-2>.sidebar-heading{border-left:none}.sidebar-heading{color:var(--textColor);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:#11a8cd;border-left-color:#11a8cd}.sidebar-heading.clickable:hover{color:#11a8cd}.sidebar-group-items{transition:height .1s ease-out;font-size:.95em;overflow:hidden}.sidebar .sidebar-sub-headers{padding-left:1rem;font-size:.95em}.sidebar .sidebar-sub-headers .level4{padding-left:.2rem}.sidebar .sidebar-sub-headers .level5{padding-left:.4rem}.sidebar .sidebar-sub-headers .level6{padding-left:.6rem}a.sidebar-link{font-size:1em;font-weight:400;display:inline-block;color:var(--textColor);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:#11a8cd}a.sidebar-link.active{font-weight:600;color:#11a8cd;border-left-color:#11a8cd}.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 var(--borderColor);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}.sidebar .blogger{display:none;border-bottom:1px solid var(--borderColor)}.sidebar .blogger img{width:60px;height:60px;border-radius:5px;margin:.75rem 1rem}.sidebar .blogger .blogger-info{flex:1;padding:0 .3rem .3rem 0}.sidebar .blogger .blogger-info h3{margin:.95rem 0 .6rem;font-size:1.1rem}.sidebar .blogger .blogger-info .icons .iconfont{font-size:1.2rem;padding-right:.6rem;color:#777}.sidebar .sidebar-slot{margin-bottom:-.5rem;font-size:.85rem}.sidebar .sidebar-slot.sidebar-slot-top{padding:1.5rem 1.5rem 0}.sidebar .sidebar-slot.sidebar-slot-bottom{padding:0 1.5rem 1.5rem}@media (max-width:719px){.sidebar .blogger{display:flex}.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}}.yellowBorder{border-radius:5px;box-shadow:0 0 15px #ffe089!important}.buttons{position:fixed;right:2rem;bottom:2.5rem;z-index:11}@media (max-width:959px){.buttons{right:1rem;bottom:1.5rem}}.buttons .button{width:2.2rem;height:2.2rem;line-height:2.2rem;border-radius:50%;box-shadow:0 2px 6px rgba(0,0,0,.15);margin-top:.9rem;text-align:center;cursor:pointer;transition:all .5s;background:var(--blurBg)}.buttons .button.hover{background:#11a8cd;box-shadow:0 0 15px #11a8cd}.buttons .button.hover:before{color:#fff}@media (any-hover:hover){.buttons .button:hover{background:#11a8cd;box-shadow:0 0 15px #11a8cd}.buttons .button:hover:before{color:#fff}}.buttons .button .select-box{margin:0;padding:.8rem 0;position:absolute;bottom:0;right:1.5rem;background:var(--mainBg);border:1px solid var(--borderColor);width:120px;border-radius:6px;box-shadow:0 0 15px hsla(0,0%,100%,.2)}.buttons .button .select-box li{list-style:none;line-height:2rem;font-size:.95rem}.buttons .button .select-box li:hover{color:#11a8cd}.buttons .button .select-box li.active{background-color:hsla(0,0%,58.8%,.2);color:#11a8cd}.mode-enter-active,.mode-leave-active{transition:all .3s}.mode-enter,.mode-leave-to{opacity:0;transform:scale(.8)}.fade-enter-active,.fade-leave-active{transition:opacity .2s}.fade-enter,.fade-leave-to{opacity:0}.footer{padding:5rem 1.5rem 2.5rem;text-align:center;color:#666;box-sizing:border-box;font-size:.85rem;transition:all .2s ease}.footer>span{line-height:1.5rem}.footer .icons{margin-bottom:12px}.footer .icons .iconfont{padding:0 10px;font-size:1.3rem}.footer a{color:inherit}.footer a:hover{color:#11a8cd}@media (min-width:720px){.sidebar-open .footer{width:auto;padding-left:19.5rem}}@media (min-width:1520px){.have-rightmenu .footer{padding-right:231.5px}}.no-sidebar .footer{width:auto;padding-left:1.5rem}.body-bg{position:fixed;left:0;top:0;z-index:-999999;height:100vh;width:100vw;transition:background .5s}.theme-mode-light{--bodyBg:#f4f4f4;--mainBg:#fff;--sidebarBg:hsla(0,0%,100%,0.8);--blurBg:hsla(0,0%,100%,0.9);--customBlockBg:#f1f1f1;--textColor:#00323c;--textLightenColor:#0085ad;--borderColor:rgba(0,0,0,0.12);--codeBg:#f6f6f6;--codeColor:#525252}.theme-mode-light code[class*=language-],.theme-mode-light pre[class*=language-]{color:#000;background:none;text-shadow:0 1px #fff;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;hyphens:none}.theme-mode-light code[class*=language-]::-moz-selection,.theme-mode-light code[class*=language-] ::-moz-selection,.theme-mode-light pre[class*=language-]::-moz-selection,.theme-mode-light pre[class*=language-] ::-moz-selection{text-shadow:none;background:#b3d4fc}.theme-mode-light code[class*=language-]::selection,.theme-mode-light code[class*=language-] ::selection,.theme-mode-light pre[class*=language-]::selection,.theme-mode-light pre[class*=language-] ::selection{text-shadow:none;background:#b3d4fc}@media print{.theme-mode-light code[class*=language-],.theme-mode-light pre[class*=language-]{text-shadow:none}}.theme-mode-light pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}.theme-mode-light :not(pre)>code[class*=language-],.theme-mode-light pre[class*=language-]{background:#f5f2f0}.theme-mode-light :not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.theme-mode-light .token.cdata,.theme-mode-light .token.comment,.theme-mode-light .token.doctype,.theme-mode-light .token.prolog{color:#708090}.theme-mode-light .token.punctuation{color:#999}.theme-mode-light .namespace{opacity:.7}.theme-mode-light .token.boolean,.theme-mode-light .token.constant,.theme-mode-light .token.deleted,.theme-mode-light .token.number,.theme-mode-light .token.property,.theme-mode-light .token.symbol,.theme-mode-light .token.tag{color:#905}.theme-mode-light .token.attr-name,.theme-mode-light .token.builtin,.theme-mode-light .token.char,.theme-mode-light .token.inserted,.theme-mode-light .token.selector,.theme-mode-light .token.string{color:#690}.theme-mode-light .language-css .token.string,.theme-mode-light .style .token.string,.theme-mode-light .token.entity,.theme-mode-light .token.operator,.theme-mode-light .token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.theme-mode-light .token.atrule,.theme-mode-light .token.attr-value,.theme-mode-light .token.keyword{color:#07a}.theme-mode-light .token.class-name,.theme-mode-light .token.function{color:#dd4a68}.theme-mode-light .token.important,.theme-mode-light .token.regex,.theme-mode-light .token.variable{color:#e90}.theme-mode-light .token.bold,.theme-mode-light .token.important{font-weight:700}.theme-mode-light .token.italic{font-style:italic}.theme-mode-light .token.entity{cursor:help}.theme-mode-light div[class*=language-] .highlight-lines .highlighted,.theme-mode-light div[class*=language-].line-numbers-mode .highlight-lines .highlighted:before{background-color:hsla(0,0%,78.4%,.4)}.theme-mode-dark{--bodyBg:#27272b;--mainBg:#1e1e22;--sidebarBg:rgba(30,30,34,0.8);--blurBg:rgba(30,30,34,0.8);--customBlockBg:#27272b;--textColor:#9b9baa;--textLightenColor:#0085ad;--borderColor:#30363d;--codeBg:#252526;--codeColor:#fff}.theme-mode-dark code[class*=language-],.theme-mode-dark pre[class*=language-]{color:#ccc;background:none;text-shadow: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;hyphens:none}.theme-mode-dark pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}.theme-mode-dark :not(pre)>code[class*=language-],.theme-mode-dark pre[class*=language-]{background:#2d2d2d}.theme-mode-dark :not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.theme-mode-dark .token.block-comment,.theme-mode-dark .token.cdata,.theme-mode-dark .token.comment,.theme-mode-dark .token.doctype,.theme-mode-dark .token.prolog{color:#999}.theme-mode-dark .token.punctuation{color:#ccc}.theme-mode-dark .token.attr-name,.theme-mode-dark .token.deleted,.theme-mode-dark .token.namespace,.theme-mode-dark .token.tag{color:#e2777a}.theme-mode-dark .token.function-name{color:#6196cc}.theme-mode-dark .token.boolean,.theme-mode-dark .token.function,.theme-mode-dark .token.number{color:#f08d49}.theme-mode-dark .token.class-name,.theme-mode-dark .token.constant,.theme-mode-dark .token.property,.theme-mode-dark .token.symbol{color:#f8c555}.theme-mode-dark .token.atrule,.theme-mode-dark .token.builtin,.theme-mode-dark .token.important,.theme-mode-dark .token.keyword,.theme-mode-dark .token.selector{color:#cc99cd}.theme-mode-dark .token.attr-value,.theme-mode-dark .token.char,.theme-mode-dark .token.regex,.theme-mode-dark .token.string,.theme-mode-dark .token.variable{color:#7ec699}.theme-mode-dark .token.entity,.theme-mode-dark .token.operator,.theme-mode-dark .token.url{color:#67cdcc}.theme-mode-dark .language-css .token.string,.theme-mode-dark .style .token.string,.theme-mode-dark .token.entity,.theme-mode-dark .token.operator,.theme-mode-dark .token.url{background:none}.theme-mode-dark .token.bold,.theme-mode-dark .token.important{font-weight:700}.theme-mode-dark .token.italic{font-style:italic}.theme-mode-dark .token.entity{cursor:help}.theme-mode-dark .token.inserted{color:green}.theme-mode-read{--bodyBg:#ececcc;--mainBg:#f5f5d5;--sidebarBg:rgba(245,245,213,0.8);--blurBg:rgba(245,245,213,0.9);--customBlockBg:#ececcc;--textColor:#704214;--textLightenColor:#963;--borderColor:rgba(0,0,0,0.15);--codeBg:#282c34;--codeColor:#fff}.theme-mode-read code[class*=language-],.theme-mode-read pre[class*=language-]{color:#ccc;background:none;text-shadow: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;hyphens:none}.theme-mode-read pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}.theme-mode-read :not(pre)>code[class*=language-],.theme-mode-read pre[class*=language-]{background:#2d2d2d}.theme-mode-read :not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.theme-mode-read .token.block-comment,.theme-mode-read .token.cdata,.theme-mode-read .token.comment,.theme-mode-read .token.doctype,.theme-mode-read .token.prolog{color:#999}.theme-mode-read .token.punctuation{color:#ccc}.theme-mode-read .token.attr-name,.theme-mode-read .token.deleted,.theme-mode-read .token.namespace,.theme-mode-read .token.tag{color:#e2777a}.theme-mode-read .token.function-name{color:#6196cc}.theme-mode-read .token.boolean,.theme-mode-read .token.function,.theme-mode-read .token.number{color:#f08d49}.theme-mode-read .token.class-name,.theme-mode-read .token.constant,.theme-mode-read .token.property,.theme-mode-read .token.symbol{color:#f8c555}.theme-mode-read .token.atrule,.theme-mode-read .token.builtin,.theme-mode-read .token.important,.theme-mode-read .token.keyword,.theme-mode-read .token.selector{color:#cc99cd}.theme-mode-read .token.attr-value,.theme-mode-read .token.char,.theme-mode-read .token.regex,.theme-mode-read .token.string,.theme-mode-read .token.variable{color:#7ec699}.theme-mode-read .token.entity,.theme-mode-read .token.operator,.theme-mode-read .token.url{color:#67cdcc}.theme-mode-read .language-css .token.string,.theme-mode-read .style .token.string,.theme-mode-read .token.entity,.theme-mode-read .token.operator,.theme-mode-read .token.url{background:none}.theme-mode-read .token.bold,.theme-mode-read .token.important{font-weight:700}.theme-mode-read .token.italic{font-style:italic}.theme-mode-read .token.entity{cursor:help}.theme-mode-read .token.inserted{color:green}.theme-style-line.theme-mode-light{--bodyBg:#fff}.theme-style-line.theme-mode-dark{--bodyBg:#1e1e22}.theme-style-line.theme-mode-read{--bodyBg:#f5f5d5}.custom-html-window{position:fixed;bottom:0;display:flex;overflow:hidden;font-weight:350}@media (max-width:960px){.custom-html-window{display:none}}.custom-html-window .custom-wrapper{position:relative;max-width:200px;max-height:400px}.custom-html-window .custom-wrapper .close-but{cursor:pointer;position:absolute;right:0;top:0;font-size:1.5rem;line-height:1.5rem;width:1.5rem;height:1.5rem;opacity:0;transition:all .2s}.custom-html-window .custom-wrapper .close-but:hover{opacity:.9}.custom-html-window .custom-wrapper:hover .close-but{opacity:.7}.custom-html-window.custom-html-window-lb{left:0;z-index:99}.custom-html-window.custom-html-window-lb>*{align-self:flex-end}.custom-html-window.custom-html-window-rb{right:80px;z-index:10;justify-content:flex-end}.custom-html-window.custom-html-window-rb>*{align-self:flex-end}.theme-mode-light[data-v-d5affa18]{--bodyBg:#f4f4f4;--mainBg:#fff;--sidebarBg:hsla(0,0%,100%,0.8);--blurBg:hsla(0,0%,100%,0.9);--customBlockBg:#f1f1f1;--textColor:#00323c;--textLightenColor:#0085ad;--borderColor:rgba(0,0,0,0.12);--codeBg:#f6f6f6;--codeColor:#525252}.theme-mode-light code[class*=language-][data-v-d5affa18],.theme-mode-light pre[class*=language-][data-v-d5affa18]{color:#000;background:none;text-shadow:0 1px #fff;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;hyphens:none}.theme-mode-light code[class*=language-][data-v-d5affa18]::-moz-selection,.theme-mode-light code[class*=language-][data-v-d5affa18] ::-moz-selection,.theme-mode-light pre[class*=language-][data-v-d5affa18]::-moz-selection,.theme-mode-light pre[class*=language-][data-v-d5affa18] ::-moz-selection{text-shadow:none;background:#b3d4fc}.theme-mode-light code[class*=language-][data-v-d5affa18]::selection,.theme-mode-light code[class*=language-][data-v-d5affa18] ::selection,.theme-mode-light pre[class*=language-][data-v-d5affa18]::selection,.theme-mode-light pre[class*=language-][data-v-d5affa18] ::selection{text-shadow:none;background:#b3d4fc}@media print{.theme-mode-light code[class*=language-][data-v-d5affa18],.theme-mode-light pre[class*=language-][data-v-d5affa18]{text-shadow:none}}.theme-mode-light pre[class*=language-][data-v-d5affa18]{padding:1em;margin:.5em 0;overflow:auto}.theme-mode-light :not(pre)>code[class*=language-][data-v-d5affa18],.theme-mode-light pre[class*=language-][data-v-d5affa18]{background:#f5f2f0}.theme-mode-light :not(pre)>code[class*=language-][data-v-d5affa18]{padding:.1em;border-radius:.3em;white-space:normal}.theme-mode-light .token.cdata[data-v-d5affa18],.theme-mode-light .token.comment[data-v-d5affa18],.theme-mode-light .token.doctype[data-v-d5affa18],.theme-mode-light .token.prolog[data-v-d5affa18]{color:#708090}.theme-mode-light .token.punctuation[data-v-d5affa18]{color:#999}.theme-mode-light .namespace[data-v-d5affa18]{opacity:.7}.theme-mode-light .token.boolean[data-v-d5affa18],.theme-mode-light .token.constant[data-v-d5affa18],.theme-mode-light .token.deleted[data-v-d5affa18],.theme-mode-light .token.number[data-v-d5affa18],.theme-mode-light .token.property[data-v-d5affa18],.theme-mode-light .token.symbol[data-v-d5affa18],.theme-mode-light .token.tag[data-v-d5affa18]{color:#905}.theme-mode-light .token.attr-name[data-v-d5affa18],.theme-mode-light .token.builtin[data-v-d5affa18],.theme-mode-light .token.char[data-v-d5affa18],.theme-mode-light .token.inserted[data-v-d5affa18],.theme-mode-light .token.selector[data-v-d5affa18],.theme-mode-light .token.string[data-v-d5affa18]{color:#690}.theme-mode-light .language-css .token.string[data-v-d5affa18],.theme-mode-light .style .token.string[data-v-d5affa18],.theme-mode-light .token.entity[data-v-d5affa18],.theme-mode-light .token.operator[data-v-d5affa18],.theme-mode-light .token.url[data-v-d5affa18]{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.theme-mode-light .token.atrule[data-v-d5affa18],.theme-mode-light .token.attr-value[data-v-d5affa18],.theme-mode-light .token.keyword[data-v-d5affa18]{color:#07a}.theme-mode-light .token.class-name[data-v-d5affa18],.theme-mode-light .token.function[data-v-d5affa18]{color:#dd4a68}.theme-mode-light .token.important[data-v-d5affa18],.theme-mode-light .token.regex[data-v-d5affa18],.theme-mode-light .token.variable[data-v-d5affa18]{color:#e90}.theme-mode-light .token.bold[data-v-d5affa18],.theme-mode-light .token.important[data-v-d5affa18]{font-weight:700}.theme-mode-light .token.italic[data-v-d5affa18]{font-style:italic}.theme-mode-light .token.entity[data-v-d5affa18]{cursor:help}.theme-mode-light div[class*=language-] .highlight-lines .highlighted[data-v-d5affa18],.theme-mode-light div[class*=language-].line-numbers-mode .highlight-lines .highlighted[data-v-d5affa18]:before{background-color:hsla(0,0%,78.4%,.4)}.theme-mode-dark[data-v-d5affa18]{--bodyBg:#27272b;--mainBg:#1e1e22;--sidebarBg:rgba(30,30,34,0.8);--blurBg:rgba(30,30,34,0.8);--customBlockBg:#27272b;--textColor:#9b9baa;--textLightenColor:#0085ad;--borderColor:#30363d;--codeBg:#252526;--codeColor:#fff}.theme-mode-dark code[class*=language-][data-v-d5affa18],.theme-mode-dark pre[class*=language-][data-v-d5affa18]{color:#ccc;background:none;text-shadow: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;hyphens:none}.theme-mode-dark pre[class*=language-][data-v-d5affa18]{padding:1em;margin:.5em 0;overflow:auto}.theme-mode-dark :not(pre)>code[class*=language-][data-v-d5affa18],.theme-mode-dark pre[class*=language-][data-v-d5affa18]{background:#2d2d2d}.theme-mode-dark :not(pre)>code[class*=language-][data-v-d5affa18]{padding:.1em;border-radius:.3em;white-space:normal}.theme-mode-dark .token.block-comment[data-v-d5affa18],.theme-mode-dark .token.cdata[data-v-d5affa18],.theme-mode-dark .token.comment[data-v-d5affa18],.theme-mode-dark .token.doctype[data-v-d5affa18],.theme-mode-dark .token.prolog[data-v-d5affa18]{color:#999}.theme-mode-dark .token.punctuation[data-v-d5affa18]{color:#ccc}.theme-mode-dark .token.attr-name[data-v-d5affa18],.theme-mode-dark .token.deleted[data-v-d5affa18],.theme-mode-dark .token.namespace[data-v-d5affa18],.theme-mode-dark .token.tag[data-v-d5affa18]{color:#e2777a}.theme-mode-dark .token.function-name[data-v-d5affa18]{color:#6196cc}.theme-mode-dark .token.boolean[data-v-d5affa18],.theme-mode-dark .token.function[data-v-d5affa18],.theme-mode-dark .token.number[data-v-d5affa18]{color:#f08d49}.theme-mode-dark .token.class-name[data-v-d5affa18],.theme-mode-dark .token.constant[data-v-d5affa18],.theme-mode-dark .token.property[data-v-d5affa18],.theme-mode-dark .token.symbol[data-v-d5affa18]{color:#f8c555}.theme-mode-dark .token.atrule[data-v-d5affa18],.theme-mode-dark .token.builtin[data-v-d5affa18],.theme-mode-dark .token.important[data-v-d5affa18],.theme-mode-dark .token.keyword[data-v-d5affa18],.theme-mode-dark .token.selector[data-v-d5affa18]{color:#cc99cd}.theme-mode-dark .token.attr-value[data-v-d5affa18],.theme-mode-dark .token.char[data-v-d5affa18],.theme-mode-dark .token.regex[data-v-d5affa18],.theme-mode-dark .token.string[data-v-d5affa18],.theme-mode-dark .token.variable[data-v-d5affa18]{color:#7ec699}.theme-mode-dark .token.entity[data-v-d5affa18],.theme-mode-dark .token.operator[data-v-d5affa18],.theme-mode-dark .token.url[data-v-d5affa18]{color:#67cdcc}.theme-mode-dark .language-css .token.string[data-v-d5affa18],.theme-mode-dark .style .token.string[data-v-d5affa18],.theme-mode-dark .token.entity[data-v-d5affa18],.theme-mode-dark .token.operator[data-v-d5affa18],.theme-mode-dark .token.url[data-v-d5affa18]{background:none}.theme-mode-dark .token.bold[data-v-d5affa18],.theme-mode-dark .token.important[data-v-d5affa18]{font-weight:700}.theme-mode-dark .token.italic[data-v-d5affa18]{font-style:italic}.theme-mode-dark .token.entity[data-v-d5affa18]{cursor:help}.theme-mode-dark .token.inserted[data-v-d5affa18]{color:green}.theme-mode-read[data-v-d5affa18]{--bodyBg:#ececcc;--mainBg:#f5f5d5;--sidebarBg:rgba(245,245,213,0.8);--blurBg:rgba(245,245,213,0.9);--customBlockBg:#ececcc;--textColor:#704214;--textLightenColor:#963;--borderColor:rgba(0,0,0,0.15);--codeBg:#282c34;--codeColor:#fff}.theme-mode-read code[class*=language-][data-v-d5affa18],.theme-mode-read pre[class*=language-][data-v-d5affa18]{color:#ccc;background:none;text-shadow: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;hyphens:none}.theme-mode-read pre[class*=language-][data-v-d5affa18]{padding:1em;margin:.5em 0;overflow:auto}.theme-mode-read :not(pre)>code[class*=language-][data-v-d5affa18],.theme-mode-read pre[class*=language-][data-v-d5affa18]{background:#2d2d2d}.theme-mode-read :not(pre)>code[class*=language-][data-v-d5affa18]{padding:.1em;border-radius:.3em;white-space:normal}.theme-mode-read .token.block-comment[data-v-d5affa18],.theme-mode-read .token.cdata[data-v-d5affa18],.theme-mode-read .token.comment[data-v-d5affa18],.theme-mode-read .token.doctype[data-v-d5affa18],.theme-mode-read .token.prolog[data-v-d5affa18]{color:#999}.theme-mode-read .token.punctuation[data-v-d5affa18]{color:#ccc}.theme-mode-read .token.attr-name[data-v-d5affa18],.theme-mode-read .token.deleted[data-v-d5affa18],.theme-mode-read .token.namespace[data-v-d5affa18],.theme-mode-read .token.tag[data-v-d5affa18]{color:#e2777a}.theme-mode-read .token.function-name[data-v-d5affa18]{color:#6196cc}.theme-mode-read .token.boolean[data-v-d5affa18],.theme-mode-read .token.function[data-v-d5affa18],.theme-mode-read .token.number[data-v-d5affa18]{color:#f08d49}.theme-mode-read .token.class-name[data-v-d5affa18],.theme-mode-read .token.constant[data-v-d5affa18],.theme-mode-read .token.property[data-v-d5affa18],.theme-mode-read .token.symbol[data-v-d5affa18]{color:#f8c555}.theme-mode-read .token.atrule[data-v-d5affa18],.theme-mode-read .token.builtin[data-v-d5affa18],.theme-mode-read .token.important[data-v-d5affa18],.theme-mode-read .token.keyword[data-v-d5affa18],.theme-mode-read .token.selector[data-v-d5affa18]{color:#cc99cd}.theme-mode-read .token.attr-value[data-v-d5affa18],.theme-mode-read .token.char[data-v-d5affa18],.theme-mode-read .token.regex[data-v-d5affa18],.theme-mode-read .token.string[data-v-d5affa18],.theme-mode-read .token.variable[data-v-d5affa18]{color:#7ec699}.theme-mode-read .token.entity[data-v-d5affa18],.theme-mode-read .token.operator[data-v-d5affa18],.theme-mode-read .token.url[data-v-d5affa18]{color:#67cdcc}.theme-mode-read .language-css .token.string[data-v-d5affa18],.theme-mode-read .style .token.string[data-v-d5affa18],.theme-mode-read .token.entity[data-v-d5affa18],.theme-mode-read .token.operator[data-v-d5affa18],.theme-mode-read .token.url[data-v-d5affa18]{background:none}.theme-mode-read .token.bold[data-v-d5affa18],.theme-mode-read .token.important[data-v-d5affa18]{font-weight:700}.theme-mode-read .token.italic[data-v-d5affa18]{font-style:italic}.theme-mode-read .token.entity[data-v-d5affa18]{cursor:help}.theme-mode-read .token.inserted[data-v-d5affa18]{color:green}.theme-style-line.theme-mode-light[data-v-d5affa18]{--bodyBg:#fff}.theme-style-line.theme-mode-dark[data-v-d5affa18]{--bodyBg:#1e1e22}.theme-style-line.theme-mode-read[data-v-d5affa18]{--bodyBg:#f5f5d5}.badge[data-v-d5affa18]{display:inline-block;font-size:14px;height:18px;line-height:18px;border-radius:3px;padding:0 6px;color:#fff}.badge.green[data-v-d5affa18],.badge.tip[data-v-d5affa18],.badge[data-v-d5affa18]{background-color:#42b983}.badge.error[data-v-d5affa18]{background-color:#da5961}.badge.warn[data-v-d5affa18],.badge.warning[data-v-d5affa18],.badge.yellow[data-v-d5affa18]{background-color:#e7c000}.badge+.badge[data-v-d5affa18]{margin-left:5px} \ No newline at end of file diff --git a/assets/img/search.83621669.svg b/assets/img/search.83621669.svg new file mode 100644 index 000000000..03d83913e --- /dev/null +++ b/assets/img/search.83621669.svg @@ -0,0 +1 @@ + diff --git a/assets/js/10.91fde849.js b/assets/js/10.91fde849.js new file mode 100644 index 000000000..0b7e644a9 --- /dev/null +++ b/assets/js/10.91fde849.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[10],{324:function(t,n,s){"use strict";s.r(n);var e=s(8),o=Object(e.a)({},(function(){return(0,this._self._c)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);n.default=o.exports}}]); \ No newline at end of file diff --git a/assets/js/100.f3dd0e2b.js b/assets/js/100.f3dd0e2b.js new file mode 100644 index 000000000..94de7df13 --- /dev/null +++ b/assets/js/100.f3dd0e2b.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[100],{411:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("blockquote",[s("p",[t._v("本文是基于 "),s("RouterLink",{attrs:{to:"/pages/e06ece/"}},[t._v("iOS-RxSwift项目实战记录")]),t._v(" 所述,如果你还未阅读过,建议你最好还先阅读一遍,并下载Demo熟悉一下 : )")],1)]),t._v(" "),s("h2",{attrs:{id:"前言"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#前言"}},[t._v("#")]),t._v(" 前言")]),t._v(" "),s("p",[t._v("MVVM的模式中,多出了ViewModel这个角色,将逻辑处理、网络请求等繁杂操作中ViewController中抽离出来,ViewController得以瘦身。\n结合RxSwift架构,我们一般就会在ViewModel中定义一个input收集繁杂操作所需的信息,通过一个transform方法将input作为参数传入,进而得到一个output供controller使用。")]),t._v(" "),s("p",[t._v("在使用RxSwift开发时会大量的使用到这种形式,其中就包括我们的网络请求。\n结合 "),s("RouterLink",{attrs:{to:"/pages/e06ece/"}},[t._v("iOS-RxSwift项目实战记录")]),t._v(" 中所述的“MJRefresh在RxSwift中的使用”,在output中定义了一个变量")],1),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" refreshStatus "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Variable")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFRefreshStatus")]),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 keyword"}},[t._v("none")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("controller通过output将其进行监听,从而当值发生变化时,controller就能实时获取当前应所处的刷新状态")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[t._v("vmOutput"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("refreshStatus"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("asObservable")]),s("span",{pre:!0,attrs:{class:"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("subscribe")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("onNext"),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 keyword"}},[t._v("weak")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" status "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("switch")]),t._v(" status "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("beingHeaderRefresh"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("tableView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("mj_header"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("beginRefreshing")]),s("span",{pre:!0,attrs:{class:"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("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("endHeaderRefresh"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("tableView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("mj_header"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("endRefreshing")]),s("span",{pre:!0,attrs:{class:"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("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("beingFooterRefresh"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("tableView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("mj_footer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("beginRefreshing")]),s("span",{pre:!0,attrs:{class:"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("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("endFooterRefresh"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("tableView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("mj_footer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("endRefreshing")]),s("span",{pre:!0,attrs:{class:"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("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("noMoreData"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("tableView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("mj_footer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("endRefreshingWithNoMoreData")]),s("span",{pre:!0,attrs:{class:"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("default")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),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(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("addDisposableTo")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("rx_disposeBag"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("如果在一个项目多处使用到了这种方式,我们就可以看到弊端——重复代码,过于冗余。")]),t._v(" "),s("p",[t._v("难道我们每次都要在controller中进行如此操作吗?")]),t._v(" "),s("h2",{attrs:{id:"面向协议"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#面向协议"}},[t._v("#")]),t._v(" 面向协议")]),t._v(" "),s("p",[t._v("关于协议的内容可以看下我之前的这两篇文章\n"),s("RouterLink",{attrs:{to:"/pages/997f68/"}},[t._v("iOS - Swift 面向协议编程(一)")])],1),t._v(" "),s("p",[s("RouterLink",{attrs:{to:"/pages/fb0480/"}},[t._v("iOS - Swift 面向协议编程(二)")])],1),t._v(" "),s("p",[t._v("总结协议的两大作用:1、规范 2、定制能力")]),t._v(" "),s("p",[t._v("定义协议 Refreshable")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* ============================ Refreshable ================================ */")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 需要使用 MJExtension 的控制器使用")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("protocol")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Refreshable")]),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("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extension")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Refreshable")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("where")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("Self")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIViewController")]),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("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("initRefreshHeader")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token omit keyword"}},[t._v("_")]),t._v(" scrollView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIScrollView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token omit keyword"}},[t._v("_")]),t._v(" action"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attribute atrule"}},[t._v("@escaping")]),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("->")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Void")]),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 class-name"}},[t._v("MJRefreshHeader")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n scrollView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("mj_header "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MJRefreshNormalHeader")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("refreshingBlock"),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 function"}},[t._v("action")]),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("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" scrollView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("mj_header\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("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("initRefreshFooter")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token omit keyword"}},[t._v("_")]),t._v(" scrollView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIScrollView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token omit keyword"}},[t._v("_")]),t._v(" action"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attribute atrule"}},[t._v("@escaping")]),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("->")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Void")]),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 class-name"}},[t._v("MJRefreshFooter")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n scrollView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("mj_footer "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MJRefreshAutoNormalFooter")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("refreshingBlock"),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 function"}},[t._v("action")]),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("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" scrollView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("mj_footer\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("在controller中遵循 Refreshable 协议,通过initRefreshHeader方法或者initRefreshFooter方法给tableView或者collectionView赋予头部或尾部刷新的能力,并且书写下拉刷新时需要执行的代码")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[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("let")]),t._v(" refreshHeader "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("initRefreshHeader")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("liveCollectionView"),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 punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("weak")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),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("self")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("vmOutput"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("requestCommand"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("onNext")]),s("span",{pre:!0,attrs:{class:"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("p",[t._v("接下来再讲讲output,只要有网络请求的地方,就会需要需要监听请求状态,既然这样,那么可以为output定义一个协议OutputRefreshProtocol,专门用来规范必需声明的属性")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* ============================ OutputRefreshProtocol ================================ */")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// viewModel 中 output使用")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("protocol")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("OutputRefreshProtocol")]),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("// 告诉外界的tableView当前的刷新状态")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" refreshStatus "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Variable")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFRefreshStatus")]),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("get")]),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("接着让output去遵循该协议,并进行初始化刷新状态的值为.none")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("struct LXFLiveOutput: OutputRefreshProtocol {\n var refreshStatus: Variable\n \n let sections: Driver<[LXFLiveSection]>\n init(sections: Driver<[LXFLiveSection]>) {\n self.sections = sections\n refreshStatus = Variable(.none)\n }\n}\n")])])]),s("p",[t._v("到此为止,其实跟之前没啥两样,只是使controller更方便初始化刷新控件而已。接下来才是本文的重点。")]),t._v(" "),s("h2",{attrs:{id:"重点"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#重点"}},[t._v("#")]),t._v(" 重点")]),t._v(" "),s("p",[t._v("刷新的状态无非也就那么几种,下拉重载数据,上拉加载更多,请求完成时结束下拉或上拉等等。。。那我们何必要在每个controller中再去管理这等琐事??\n而至此,刷新控件的状态是由变量 refreshStatus 来决定,此时 refreshStatus 又声明在 OutputRefreshProtocol 协议中,我们何不再定义一个方法,将刷新控件的状态交给refreshStatus自己来帮我们处理呢~")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extension")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("OutputRefreshProtocol")]),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("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("autoSetRefreshHeaderStatus")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("header"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MJRefreshHeader")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" footer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MJRefreshFooter")]),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 operator"}},[t._v("->")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Disposable")]),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(" refreshStatus"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("asObservable")]),s("span",{pre:!0,attrs:{class:"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("subscribe")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("onNext"),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 punctuation"}},[t._v("(")]),t._v("status"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("switch")]),t._v(" status "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("beingHeaderRefresh"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n header"),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("beginRefreshing")]),s("span",{pre:!0,attrs:{class:"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("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("endHeaderRefresh"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n header"),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("endRefreshing")]),s("span",{pre:!0,attrs:{class:"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("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("beingFooterRefresh"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n footer"),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("beginRefreshing")]),s("span",{pre:!0,attrs:{class:"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("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("endFooterRefresh"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n footer"),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("endRefreshing")]),s("span",{pre:!0,attrs:{class:"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("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("noMoreData"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n footer"),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("endRefreshingWithNoMoreData")]),s("span",{pre:!0,attrs:{class:"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("default")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),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("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("这时需要我们将刷新控件的对象 header / footer 传入到方法中,实现自动控制刷新控件状态。")]),t._v(" "),s("h2",{attrs:{id:"总结使用"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#总结使用"}},[t._v("#")]),t._v(" 总结使用")]),t._v(" "),s("p",[t._v("一、output中遵守协议 OutputRefreshProtocol, 并初始化 refreshStatus 的值为 none")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFLiveOutput")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("OutputRefreshProtocol")]),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(" refreshStatus"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Variable")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFRefreshStatus")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n \n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" sections"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Driver")]),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 class-name"}},[t._v("LXFLiveSection")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("init")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("sections"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Driver")]),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 class-name"}},[t._v("LXFLiveSection")]),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 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("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sections "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" sections\n refreshStatus "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Variable")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFRefreshStatus")]),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 keyword"}},[t._v("none")]),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("二、controller 遵守协议 Refreshable,通过协议中的方法初始化刷新控件及对应的操作,并将刷新控件对象作为参数传入到自动处理状态方法中")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extension")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFLiveViewController")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Refreshable")]),t._v(" \n")])])]),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" refreshHeader "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("initRefreshHeader")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("liveCollectionView"),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 punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("weak")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("vmOutput"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("requestCommand"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("onNext")]),s("span",{pre:!0,attrs:{class:"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("\nvmOutput"),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("autoSetRefreshHeaderStatus")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("header"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" refreshHeader"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" footer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),s("span",{pre:!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("disposed")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("by"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" rx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("disposeBag"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("三、viewModel中根据实际情况实时更新 refreshStatus 的刷新状态")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110213748.png",alt:"image.png"}})]),t._v(" "),s("h2",{attrs:{id:"案例"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#案例"}},[t._v("#")]),t._v(" 案例")]),t._v(" "),s("p",[t._v("协议:"),s("a",{attrs:{href:"https://github.com/LinXunFeng/LXFBiliBili/blob/master/LXFBiliBili/LXFBiliBili/Classes/Common/Protocol/Lib/Refreshable.swift",target:"_blank",rel:"noopener noreferrer"}},[t._v("Refreshable.swift"),s("OutboundLink")],1),t._v("\nViewModel:"),s("a",{attrs:{href:"https://github.com/LinXunFeng/LXFBiliBili/blob/master/LXFBiliBili/LXFBiliBili/Classes/Main/Home/Controller/Live/ViewModel/LXFLiveViewModel.swift",target:"_blank",rel:"noopener noreferrer"}},[t._v("LXFLiveViewModel"),s("OutboundLink")],1),t._v("\nController:"),s("a",{attrs:{href:"https://github.com/LinXunFeng/LXFBiliBili/blob/master/LXFBiliBili/LXFBiliBili/Classes/Main/Home/Controller/Live/LXFLiveViewController.swift",target:"_blank",rel:"noopener noreferrer"}},[t._v("LXFLiveViewController"),s("OutboundLink")],1)]),t._v(" "),s("p",[s("a",{attrs:{href:"https://github.com/LinXunFeng/LXFBiliBili",target:"_blank",rel:"noopener noreferrer"}},[t._v("LXFBiliBili"),s("OutboundLink")],1)]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110213759.gif",alt:"LXFBiliBili"}})])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/101.21943b2d.js b/assets/js/101.21943b2d.js new file mode 100644 index 000000000..d781bbc71 --- /dev/null +++ b/assets/js/101.21943b2d.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[101],{413:function(t,s,r){"use strict";r.r(s);var a=r(8),e=Object(a.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h1",{attrs:{id:"高仿喜马拉雅fm"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#高仿喜马拉雅fm"}},[t._v("#")]),t._v(" 高仿喜马拉雅FM")]),t._v(" "),s("p",[t._v("gitHub: https://github.com/LinXunFeng/LXFFM")]),t._v(" "),s("h2",{attrs:{id:"说明"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#说明"}},[t._v("#")]),t._v(" 说明")]),t._v(" "),s("p",[t._v("基于Swift 3.0 编写而成,运行环境要求: Xcode 8.0")]),t._v(" "),s("h2",{attrs:{id:"lxffm"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#lxffm"}},[t._v("#")]),t._v(" LXFFM")]),t._v(" "),s("p",[t._v("原OC版: "),s("a",{attrs:{href:"https://github.com/Eastwu5788/XMLYFM",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/Eastwu5788/XMLYFM"),s("OutboundLink")],1),t._v("\n从原版作者的源代码上学到了很多,十分感谢.\n由于只是本人OC转Swift的试练项目,所以有些内容并没有实现,哈哈")]),t._v(" "),s("h3",{attrs:{id:"效果图"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#效果图"}},[t._v("#")]),t._v(" 效果图")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://github.com/LinXunFeng/LXFFM/raw/master/Screenshots/1.gif",alt:"\b推荐"}})]),t._v(" "),s("p",[s("img",{attrs:{src:"https://github.com/LinXunFeng/LXFFM/raw/master/Screenshots/2.gif",alt:"\b分类"}})]),t._v(" "),s("p",[s("img",{attrs:{src:"https://github.com/LinXunFeng/LXFFM/raw/master/Screenshots/3.gif",alt:"广播"}})]),t._v(" "),s("p",[s("img",{attrs:{src:"https://github.com/LinXunFeng/LXFFM/raw/master/Screenshots/4.gif",alt:"榜单"}})]),t._v(" "),s("p",[s("img",{attrs:{src:"https://github.com/LinXunFeng/LXFFM/raw/master/Screenshots/5.gif",alt:"\b主播"}})]),t._v(" "),s("p",[s("img",{attrs:{src:"https://github.com/LinXunFeng/LXFFM/raw/master/Screenshots/6.gif",alt:"\b订阅听"}})]),t._v(" "),s("p",[s("img",{attrs:{src:"https://github.com/LinXunFeng/LXFFM/raw/master/Screenshots/7.gif",alt:"\b下载听"}})]),t._v(" "),s("p",[s("img",{attrs:{src:"https://github.com/LinXunFeng/LXFFM/raw/master/Screenshots/8.gif",alt:"我的"}})]),t._v(" "),s("div",{staticClass:"github-widget",attrs:{"data-repo":"LinXunFeng/LXFFM"}})])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/102.2bc77eeb.js b/assets/js/102.2bc77eeb.js new file mode 100644 index 000000000..baccfd58b --- /dev/null +++ b/assets/js/102.2bc77eeb.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[102],{414:function(t,a,s){"use strict";s.r(a);var n=s(8),r=Object(n.a)({},(function(){var t=this,a=t._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"lxfwechat"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#lxfwechat"}},[t._v("#")]),t._v(" LXFWeChat")]),t._v(" "),a("p",[t._v("Swift 3.0 高仿微信")]),t._v(" "),a("blockquote",[a("p",[t._v("两个测试账号: lxf lqr  密码都是123456")])]),t._v(" "),a("h2",{attrs:{id:"源码地址"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#源码地址"}},[t._v("#")]),t._v(" 源码地址")]),t._v(" "),a("p",[t._v("码云\nhttp://git.oschina.net/LinXunFeng/LXFWeChat")]),t._v(" "),a("p",[t._v("GitHub\nhttps://github.com/LinXunFeng/LXFWeChat")]),t._v(" "),a("p",[t._v("在此提供存放于百度云的完整项目"),a("a",{attrs:{href:"https://pan.baidu.com/s/1bpB55Bx",target:"_blank",rel:"noopener noreferrer"}},[t._v("【\b高仿微信】- 百度云"),a("OutboundLink")],1),t._v("\n希望各位能在我的项目上献出一个宝贵的Star\n谢谢")]),t._v(" "),a("blockquote",[a("p",[t._v("从2016年12月份开始做到现在,虽说这个项目看起来不大,但是锻炼人的地方还真是很多的,微信通讯录联系人的按拼音排序,自定义表情键盘中遇到的分页滚动,微信聊天界面的图片显示、语音信息的动画等。挑战着各种各样的需求,本人也将遇到的问题的解决方案做了记录并整理了一下,希望能给iOS程序员同胞们带来帮助。目前基本的聊天已完成,实现了一部分个人信息的修改,功能我会尽快完善的!")])]),t._v(" "),a("h2",{attrs:{id:"模仿微信的导航栏"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#模仿微信的导航栏"}},[t._v("#")]),t._v(" 模仿微信的导航栏")]),t._v(" "),a("p",[t._v("在navigationBar底部添加一个添加了渐变层的view")]),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("let")]),t._v(" blurBackView "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIView")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nblurBackView"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("frame "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CGRect")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),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(" y"),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 number"}},[t._v("20")]),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 constant"}},[t._v("kScreenW")]),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("64")]),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(" gradintLayer "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CAGradientLayer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\ngradintLayer"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("frame "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CGRect")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),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(" y"),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(" width"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token constant"}},[t._v("kScreenW")]),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("64")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\ngradintLayer"),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 punctuation"}},[t._v("[")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIColor")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("hexInt")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x040012")]),a("span",{pre:!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("withAlphaComponent")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0.76")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("cgColor"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIColor")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("hexInt")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x040012")]),a("span",{pre:!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("withAlphaComponent")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0.28")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("cgColor\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\ngradintLayer"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("startPoint "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CGPoint")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),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(" y"),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("\ngradintLayer"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("endPoint "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CGPoint")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),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(" y"),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("\nblurBackView"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("layer"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("addSublayer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("gradintLayer"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nblurBackView"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isUserInteractionEnabled "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),t._v("\nblurBackView"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("alpha "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0.5")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 设置导航栏样式")]),t._v("\nnavigationBar"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("barStyle "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("black\nnavigationBar"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("insertSubview")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("blurBackView"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" at"),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")])])]),a("h2",{attrs:{id:"表情面板和更多面板"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#表情面板和更多面板"}},[t._v("#")]),t._v(" 表情面板和更多面板")]),t._v(" "),a("p",[t._v("遇到的问题总结了一下,可以参考下以下总结的文章")]),t._v(" "),a("p",[a("RouterLink",{attrs:{to:"/pages/d0a26b/"}},[t._v("《iOS - Swift UICollectionView横向分页滚动,cell左右排版》")])],1),t._v(" "),a("p",[a("RouterLink",{attrs:{to:"/pages/f6bde9/"}},[t._v("《iOS-Swift-UICollectionView横向分页的问题》")])],1),t._v(" "),a("h2",{attrs:{id:"聊天界面"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#聊天界面"}},[t._v("#")]),t._v(" 聊天界面")]),t._v(" "),a("p",[a("RouterLink",{attrs:{to:"/pages/0909a3/"}},[t._v("《iOS-Swift 仿微信聊天图片显示》")])],1),t._v(" "),a("p",[a("RouterLink",{attrs:{to:"/pages/1cb0c1/"}},[t._v('《iOS-Swift-UITableView的scrollToRow的"坑"》')])],1),t._v(" "),a("p",[a("RouterLink",{attrs:{to:"/pages/832f44/"}},[t._v("《iOS-Swift-UIButton中ImageView的animationImages动画执行完毕后,图标变暗》")])],1),t._v(" "),a("h2",{attrs:{id:"首页"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#首页"}},[t._v("#")]),t._v(" 首页")]),t._v(" "),a("p",[a("RouterLink",{attrs:{to:"/pages/cb27eb/"}},[t._v("《iOS - Swift 仿微信小红点(无数字)》")])],1),t._v(" "),a("h2",{attrs:{id:"目前完成的功能"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#目前完成的功能"}},[t._v("#")]),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("显示右上角的菜单")]),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("ol",[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("ol",[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("ol",[a("li",[t._v("个人头像的上传与设置,及头像的保存")]),t._v(" "),a("li",[t._v("我的二维码界面的显示及二维码的保存")])]),t._v(" "),a("h3",{attrs:{id:"聊天界面-2"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#聊天界面-2"}},[t._v("#")]),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("li",[t._v("图片的发送与显示")]),t._v(" "),a("li",[t._v("未发送成功的重发功能")]),t._v(" "),a("li",[t._v("语音的录制与发送")]),t._v(" "),a("li",[t._v("语音的播放动态效果")])]),t._v(" "),a("h2",{attrs:{id:"已知bug"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#已知bug"}},[t._v("#")]),t._v(" 已知BUG")]),t._v(" "),a("ul",[a("li",[t._v("小视频和图片发送出去后不能立即更新显示缩略图")]),t._v(" "),a("li",[t._v("更换头像模拟器测试正常,真机无效。。")])]),t._v(" "),a("h2",{attrs:{id:"_2017-07-24-更新"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_2017-07-24-更新"}},[t._v("#")]),t._v(" 2017-07-24 更新")]),t._v(" "),a("p",[t._v("抽空出来添加了一个简单的直播功能(对方需要先进入到对应的聊天界面)\n相关博文:"),a("RouterLink",{attrs:{to:"/pages/7b7220/"}},[t._v("iOS-给高仿微信添加直播聊天功能")]),t._v("\n需要用到"),a("a",{attrs:{href:"https://github.com/LinXunFeng/IJKFramework",target:"_blank",rel:"noopener noreferrer"}},[t._v("编译好的B站开源库ijkplayer"),a("OutboundLink")],1),t._v(" ,由于打包好的文件太大,传不上来,所以需要各位去自己编译集成进去。")],1),t._v(" "),a("h2",{attrs:{id:"效果图"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#效果图"}},[t._v("#")]),t._v(" 效果图")]),t._v(" "),a("h3",{attrs:{id:"动态图"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#动态图"}},[t._v("#")]),t._v(" 动态图")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://github.com/LinXunFeng/LXFWeChat/raw/master/Screenshots/1.gif",alt:""}}),t._v(" "),a("img",{attrs:{src:"https://github.com/LinXunFeng/LXFWeChat/raw/master/Screenshots/2.gif",alt:""}}),t._v(" "),a("img",{attrs:{src:"https://github.com/LinXunFeng/LXFWeChat/raw/master/Screenshots/3.gif",alt:""}}),t._v(" "),a("img",{attrs:{src:"https://github.com/LinXunFeng/LXFWeChat/raw/master/Screenshots/4.gif",alt:""}}),t._v(" "),a("img",{attrs:{src:"https://github.com/LinXunFeng/LXFWeChat/raw/master/Screenshots/5.gif",alt:""}}),t._v(" "),a("img",{attrs:{src:"https://github.com/LinXunFeng/LXFWeChat/raw/master/Screenshots/6.gif",alt:""}}),t._v(" "),a("img",{attrs:{src:"https://github.com/LinXunFeng/LXFWeChat/raw/master/Screenshots/7.gif",alt:""}})]),t._v(" "),a("h3",{attrs:{id:"静态图"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#静态图"}},[t._v("#")]),t._v(" 静态图")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://github.com/LinXunFeng/LXFWeChat/raw/master/Screenshots/Snip20170206_1.png",alt:""}}),t._v(" "),a("img",{attrs:{src:"https://github.com/LinXunFeng/LXFWeChat/raw/master/Screenshots/Snip20170214_1.png",alt:""}}),t._v(" "),a("img",{attrs:{src:"https://github.com/LinXunFeng/LXFWeChat/raw/master/Screenshots/Snip20170214_2.png",alt:""}}),t._v(" "),a("img",{attrs:{src:"https://github.com/LinXunFeng/LXFWeChat/raw/master/Screenshots/Snip20170214_3.png",alt:""}}),t._v(" "),a("img",{attrs:{src:"https://github.com/LinXunFeng/LXFWeChat/raw/master/Screenshots/Snip20170214_4.png",alt:""}}),t._v(" "),a("img",{attrs:{src:"https://github.com/LinXunFeng/LXFWeChat/raw/master/Screenshots/Snip20170214_5.png",alt:""}}),t._v(" "),a("img",{attrs:{src:"https://github.com/LinXunFeng/LXFWeChat/raw/master/Screenshots/Snip20170214_6.png",alt:""}}),t._v(" "),a("img",{attrs:{src:"https://github.com/LinXunFeng/LXFWeChat/raw/master/Screenshots/Snip20170214_7.png",alt:""}}),t._v(" "),a("img",{attrs:{src:"https://github.com/LinXunFeng/LXFWeChat/raw/master/Screenshots/Snip20170214_8.png",alt:""}}),t._v(" "),a("img",{attrs:{src:"https://github.com/LinXunFeng/LXFWeChat/raw/master/Screenshots/Snip20170214_9.png",alt:""}})]),t._v(" "),a("div",{staticClass:"github-widget",attrs:{"data-repo":"LinXunFeng/LXFWeChat"}})])}),[],!1,null,null,null);a.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/103.5a9b8fdf.js b/assets/js/103.5a9b8fdf.js new file mode 100644 index 000000000..8bb966146 --- /dev/null +++ b/assets/js/103.5a9b8fdf.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[103],{416:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("blockquote",[s("p",[t._v("为了良好的交互体验,相信大家在对待"),s("code",[t._v("scrollView")]),t._v("无数据时的提示页都会使用一些第三方来定制,最典型的就是使用"),s("a",{attrs:{href:"https://github.com/dzenbot/DZNEmptyDataSet",target:"_blank",rel:"noopener noreferrer"}},[t._v("DZNEmptyDataSet"),s("OutboundLink")],1),t._v("。但是每个界面都写一堆与"),s("code",[t._v("DZNEmptyDataSetDelegate")]),t._v(","),s("code",[t._v("DZNEmptyDataSetSource")]),t._v("相关的代码就不太好,那一般情况下自然的就会采用继承的方式来避免。而Swift除了可以面向对象编程,它还可以面向协议编程。那可不可以也用协议来解决情况呢?嘿嘿,这个可以有,那我们接下来就来试试怎么通过协议的方式来避免上述情况,并且实现一行代码添加空白页功能")])]),t._v(" "),s("h2",{attrs:{id:"前言"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#前言"}},[t._v("#")]),t._v(" 前言")]),t._v(" "),s("p",[t._v("如果对面向协议有疑问的同学可以看下我之前的两篇文章")]),t._v(" "),s("p",[s("RouterLink",{attrs:{to:"/pages/997f68/"}},[t._v("iOS - Swift 面向协议编程(一)")])],1),t._v(" "),s("p",[s("RouterLink",{attrs:{to:"/pages/fb0480/"}},[t._v("iOS - Swift 面向协议编程(二)")])],1),t._v(" "),s("p",[t._v("之前的文章中提到了,协议除了起规范作用,还有别一个用处,就是赋予能力。我们现在的目的就是让目标控制器或者目标视图在遵守我们的协议后,就可以有实现空白页的功能。")]),t._v(" "),s("h2",{attrs:{id:"一、基本实现"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#一、基本实现"}},[t._v("#")]),t._v(" 一、基本实现")]),t._v(" "),s("h3",{attrs:{id:"_1、创建协议"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、创建协议"}},[t._v("#")]),t._v(" 1、创建协议")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// MARK:- 空视图占位协议")]),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("protocol")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFEmptyDataSetable")]),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("}")]),t._v("\n")])])]),s("h3",{attrs:{id:"_2、确定面向类"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、确定面向类"}},[t._v("#")]),t._v(" 2、确定面向类")]),t._v(" "),s("p",[t._v("确定我们面向的类,一般"),s("code",[t._v("tableView")]),t._v("或者"),s("code",[t._v("collectionView")]),t._v("都是写在控制器里,那我们面向的类就规定为"),s("code",[t._v("UIViewController")]),t._v(",或许也有人写在"),s("code",[t._v("UIView")]),t._v("里,不过这里先按"),s("code",[t._v("UIViewController")]),t._v("来写吧")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// MARK:- UIViewController - 空视图占位协议")]),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("extension")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFEmptyDataSetable")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("where")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("Self")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIViewController")]),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 punctuation"}},[t._v("}")]),t._v("\n")])])]),s("h3",{attrs:{id:"_3、定义功能方法"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_3、定义功能方法"}},[t._v("#")]),t._v(" 3、定义功能方法")]),t._v(" "),s("p",[t._v("将"),s("code",[t._v("scrollView")]),t._v("传递进来,让我们定义的方法来暗地里做些操作")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("lxf_EmptyDataSet")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token omit keyword"}},[t._v("_")]),t._v(" scrollView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIScrollView")]),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 scrollView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("emptyDataSetDelegate "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),t._v("\n scrollView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("emptyDataSetSource "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("h3",{attrs:{id:"_4、设置数据源和代理"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_4、设置数据源和代理"}},[t._v("#")]),t._v(" 4、设置数据源和代理")]),t._v(" "),s("p",[t._v("在"),s("code",[t._v("3、定义功能方法")]),t._v("中将"),s("code",[t._v("delegate")]),t._v("和"),s("code",[t._v("source")]),t._v("设置为了"),s("code",[t._v("self")]),t._v(" ,而协议是无法遵守再次遵守其它协议的,那让什么来遵守对应的协议呢?要明白这里的"),s("code",[t._v("self")]),t._v("指的是"),s("code",[t._v("UIViewController")]),t._v(",考虑到"),s("code",[t._v("UIView")]),t._v("的可能,这里我就让万物对象之父"),s("code",[t._v("NSObject")]),t._v("来遵守,并实现对应的数据源方法和代理方法")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extension")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("NSObject")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DZNEmptyDataSetDelegate")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DZNEmptyDataSetSource")]),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("public")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("image")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("forEmptyDataSet scrollView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIScrollView")]),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 operator"}},[t._v("->")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIImage")]),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 comment"}},[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("public")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("title")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("forEmptyDataSet scrollView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIScrollView")]),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 operator"}},[t._v("->")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("NSAttributedString")]),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 comment"}},[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("public")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("verticalOffset")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("forEmptyDataSet scrollView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIScrollView")]),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 operator"}},[t._v("->")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CGFloat")]),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("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),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("UIViewController")]),t._v("遵守我们的协议,再调用一下"),s("code",[t._v("lxf_EmptyDataSet")]),t._v("方法就可以实现数据空白页了。但是,这样直接写死的方式很不好,有时候一些场景是需要我们做出定制的,那怎么实现定制呢?协议又不能有自己的变量来存放我们的定制。")]),t._v(" "),s("p",[s("font",{attrs:{color:"red"}},[s("em",[t._v("这里先做出一个限定,我们要使用重载方法来完成该功能,实现即可高定制,又可使用默认定制。")])])],1),t._v(" "),s("p",[t._v("回到刚刚的话题,使用UserDefaults来实现可以吗?可以,但是比较麻烦,因为UserDefaults是单例,整个进程共用这一份资源,如果你当前"),s("code",[t._v("controller")]),t._v("遵守了我们的协议"),s("code",[t._v("LXFEmptyDataSetable")]),t._v("并做出了定制,那么当下一个"),s("code",[t._v("controller")]),t._v("在遵守协议后使用了"),s("code",[t._v("默认定制")]),t._v("时,那你要怎么办?还要区分"),s("code",[t._v("scrollView")]),t._v(",那就得保存当前"),s("code",[t._v("scrollView")]),t._v(",在退出当前"),s("code",[t._v("controller")]),t._v("后还要把对应的东西置空。好咯好咯,那你说到底要怎么搞才最合适?")]),t._v(" "),s("blockquote",[s("p",[t._v("解决方案:拓展"),s("code",[t._v("UIScrollView")]),t._v("!!!有没有发现?,非常地恰巧,我们定义的方法"),s("code",[t._v("lxf_EmptyDataSet")]),t._v("需要外界将"),s("code",[t._v("UIScrollView")]),t._v("传递进来,在"),s("code",[t._v("DZNEmptyDataSet")]),t._v("的数据源方法和代理方法也有"),s("code",[t._v("scrollView")]),t._v("。那让"),s("code",[t._v("UIScrollView")]),t._v("来携带我们的定制就好啦。")])]),t._v(" "),s("h3",{attrs:{id:"_1、定义定制相关的枚举"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、定义定制相关的枚举"}},[t._v("#")]),t._v(" 1、定义定制相关的枚举")]),t._v(" "),s("p",[t._v("这里我定义了常用的定制相关的枚举")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("public")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFEmptyDataSetAttributeKeyType")]),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("/// 纵向偏移(-50) CGFloat")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" verticalOffset\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 提示语(暂无数据) String")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" tipStr\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 提示语的font(system15) UIFont")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" tipFont\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 提示语颜色(D2D2D2) UIColor")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" tipColor\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 提示图(LXFEmptyDataPic) UIImage")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" tipImage\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 允许滚动(true) Bool")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" allowScroll\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("h3",{attrs:{id:"_2、拓展uiscrollview"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、拓展uiscrollview"}},[t._v("#")]),t._v(" 2、拓展UIScrollView")]),t._v(" "),s("p",[t._v("为"),s("code",[t._v("UIScrollView")]),t._v("定义一个定制相关的属性字典")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extension")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIScrollView")]),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("struct")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AssociatedKeys")]),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("var")]),t._v(" lxf_emptyAttributeDict"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFEmptyDataSetAttributeKeyType")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token operator"}},[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 keyword"}},[t._v("var")]),t._v(" lxf_emptyAttributeDict"),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 class-name"}},[t._v("LXFEmptyDataSetAttributeKeyType")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),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("get")]),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("objc_getAssociatedObject")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),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 class-name"}},[t._v("AssociatedKeys")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("lxf_emptyAttributeDict"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),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("LXFEmptyDataSetAttributeKeyType")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("Any")]),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("set")]),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("objc_setAssociatedObject")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),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 class-name"}},[t._v("AssociatedKeys")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("lxf_emptyAttributeDict"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" newValue "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFEmptyDataSetAttributeKeyType")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("Any")]),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 punctuation"}},[t._v(",")]),t._v(" objc_AssociationPolicy"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token constant"}},[t._v("OBJC_ASSOCIATION_RETAIN_NONATOMIC")]),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("h3",{attrs:{id:"_3、完善lxf-emptydataset方法"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_3、完善lxf-emptydataset方法"}},[t._v("#")]),t._v(" 3、完善lxf_EmptyDataSet方法")]),t._v(" "),s("p",[t._v("这里我们让外界通过闭包的方式来定制自己的空白页")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// MARK:- UIViewController - 空视图占位协议")]),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("extension")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFEmptyDataSetable")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("where")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("Self")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIViewController")]),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("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("lxf_EmptyDataSet")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token omit keyword"}},[t._v("_")]),t._v(" scrollView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIScrollView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" attributeBlock"),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(")")]),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 class-name"}},[t._v("LXFEmptyDataSetAttributeKeyType")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"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 operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),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 scrollView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("lxf_emptyAttributeDict "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" attributeBlock "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" attributeBlock"),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(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),t._v("\n scrollView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("emptyDataSetDelegate "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),t._v("\n scrollView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("emptyDataSetSource "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),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("h3",{attrs:{id:"_4、使用定制属性字典"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_4、使用定制属性字典"}},[t._v("#")]),t._v(" 4、使用定制属性字典")]),t._v(" "),s("p",[t._v("这里以返回提示图片的方法为例吧")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v('public func image(forEmptyDataSet scrollView: UIScrollView!) -> UIImage! {\n guard let tipImg = scrollView.lxf_emptyAttributeDict?[.tipImage] as? UIImage else {\n return UIImage(named: "LXFEmptyDataPic")\n }\n return tipImg\n}\n')])])]),s("h3",{attrs:{id:"_5、外界的使用姿势"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_5、外界的使用姿势"}},[t._v("#")]),t._v(" 5、外界的使用姿势")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[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("LXFEmptyDemoController")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIViewController")]),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("override")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("viewDidLoad")]),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("viewDidLoad")]),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 function"}},[t._v("initUI")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 keyword"}},[t._v("extension")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFEmptyDemoController")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFEmptyDataSetable")]),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("fileprivate")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("initUI")]),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("let")]),t._v(" tableView "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UITableView")]),s("span",{pre:!0,attrs:{class:"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 \n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 高定制")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("lxf_EmptyDataSet")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("tableView"),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 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 punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFEmptyDataSetAttributeKeyType")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("Any")]),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("in")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._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("tipStr"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[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 punctuation"}},[t._v(".")]),t._v("verticalOffset"),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("150")]),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("allowScroll"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),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("// 默认定制")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// self.lxf_EmptyDataSet(tableView)")]),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",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110215949.png",alt:"大功告成"}})]),t._v(" "),s("h2",{attrs:{id:"三、开源库"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#三、开源库"}},[t._v("#")]),t._v(" 三、开源库")]),t._v(" "),s("p",[t._v("我对这个过程进行一次整理,并做成一个名为 "),s("a",{attrs:{href:"https://github.com/LinXunFeng/LXFProtocolTool",target:"_blank",rel:"noopener noreferrer"}},[t._v("LXFProtocolTool"),s("OutboundLink")],1),t._v(" 的库并上传至gitHub。可以使用"),s("code",[t._v("Cocoapods")]),t._v("的方式来安装使用")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("pod 'LXFProtocolTool'\n")])])]),s("p",[t._v("我也将 "),s("RouterLink",{attrs:{to:"/pages/fb0480/"}},[t._v("iOS - Swift 面向协议编程(二)")]),t._v(" 中提及的通过协议便捷加载xib的功能也集成了进来。大家可以根据自己的需要在Podfile写明要安装的功能")],1),t._v(" "),s("ul",[s("li",[t._v("Xib加载")])]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("pod 'LXFProtocolTool/LXFNibloadable'\n")])])]),s("ul",[s("li",[t._v("空白视图")])]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("pod 'LXFProtocolTool/LXFEmptyDataSetable'\n")])])]),s("p",[t._v("创建这个库的目的是为了通过协议的方式来方便快捷地实现一些的实用功能,目前功能不多,不过往后会逐渐增加,或许你有什么想实现的功能也可以提出来,喜欢的就给个Star鼓励下我吧 🚀 🚀 🚀")])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/104.9e5262e1.js b/assets/js/104.9e5262e1.js new file mode 100644 index 000000000..47ec997be --- /dev/null +++ b/assets/js/104.9e5262e1.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[104],{415:function(s,t,a){"use strict";a.r(t);var n=a(8),e=Object(n.a)({},(function(){var s=this,t=s._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[t("h2",{attrs:{id:"一、概述"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#一、概述"}},[s._v("#")]),s._v(" 一、概述")]),s._v(" "),t("p",[s._v("1、相信大家在使用Swift开发时,"),t("a",{attrs:{href:"https://github.com/Moya/Moya",target:"_blank",rel:"noopener noreferrer"}},[s._v("Moya"),t("OutboundLink")],1),s._v("是首选的网络工具,在模型解析这一块,Swift版模型解析的相关第三方库有很多,本人最习惯用的就是"),t("a",{attrs:{href:"https://github.com/SwiftyJSON/SwiftyJSON",target:"_blank",rel:"noopener noreferrer"}},[s._v("SwiftyJSON"),t("OutboundLink")],1),s._v("。")]),s._v(" "),t("p",[s._v("2、下面会开始讲解整个主要的开发功能与思想。")]),s._v(" "),t("p",[s._v("3、以下内容是基于大家会使用Moya和SwiftJSON的前提下所著,还不会的同学可以先简单了解后再来阅读本篇文章哦~")]),s._v(" "),t("h2",{attrs:{id:"二、功能开发与思想讲解"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#二、功能开发与思想讲解"}},[s._v("#")]),s._v(" 二、功能开发与思想讲解")]),s._v(" "),t("h3",{attrs:{id:"_1、尝试模型解析"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1、尝试模型解析"}},[s._v("#")]),s._v(" 1、尝试模型解析")]),s._v(" "),t("p",[s._v("Moya请求服务器返回的数据以Response类返回给我们,那我们就给Response类做一个扩展,这里以解析模型为例")]),s._v(" "),t("div",{staticClass:"language-swift extra-class"},[t("pre",{pre:!0,attrs:{class:"language-swift"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 需要传入一个参数,告知我们要转换出什么模型")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("func")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function-definition function"}},[s._v("mapObject")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("T")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Modelable")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token omit keyword"}},[s._v("_")]),s._v(" type"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("T")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("Type")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("->")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("T")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 模型解析过程")]),s._v("\n 。。。\n \n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("T")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])])]),t("p",[s._v("Q: 那中间的解析过程该怎么写呢?")]),s._v(" "),t("p",[s._v("A: "),t("strong",[s._v("可以让开发者遵守某个协议,实现指定的转换方法并描述转换关系。其转换过程我们不需要知道,交给开发者即可。")])]),s._v(" "),t("p",[s._v("那接着我们来定义一个协议Modelable,并声明转换方法")]),s._v(" "),t("div",{staticClass:"language-swift extra-class"},[t("pre",{pre:!0,attrs:{class:"language-swift"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("protocol")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Modelable")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("mutating")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("func")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function-definition function"}},[s._v("mapping")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token omit keyword"}},[s._v("_")]),s._v(" json"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token constant"}},[s._v("JSON")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])])]),t("p",[s._v("开发者创建一个"),t("code",[s._v("MyMoel")]),s._v("的结构体,遵守协议"),t("code",[s._v("Modelable")]),s._v(",并实现"),t("code",[s._v("mapping")]),s._v(",书写转换关系")]),s._v(" "),t("div",{staticClass:"language-swift extra-class"},[t("pre",{pre:!0,attrs:{class:"language-swift"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("struct")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("MyModel")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Modelable")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("var")]),s._v(" _id "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string-literal"}},[t("span",{pre:!0,attrs:{class:"token string"}},[s._v('""')])]),s._v("\n \n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("mutating")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("func")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function-definition function"}},[s._v("mapping")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token omit keyword"}},[s._v("_")]),s._v(" json"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token constant"}},[s._v("JSON")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("self")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("_id "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" json"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token string-literal"}},[t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"_id"')])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("stringValue\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])])]),t("p",[s._v("以目前的现状来分析一下:"),t("code",[s._v("mapObject")]),s._v("可以让开发者传入"),t("code",[s._v("模型类型")]),s._v(",而我们的协议方法却并非是个类方法。那我们需要先得到这个"),t("code",[s._v("模型类型")]),s._v("的对象,再来调用"),t("code",[s._v("mapping")]),s._v("方法")]),s._v(" "),t("h3",{attrs:{id:"_2、模型解析的驱动开发"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2、模型解析的驱动开发"}},[s._v("#")]),s._v(" 2、模型解析的驱动开发")]),s._v(" "),t("p",[s._v("Q: 怎么得到这个对象?")]),s._v(" "),t("p",[s._v("A: "),t("strong",[s._v("可以在协议中声明一个初始化方法来创建对象。是的,我们在mapObject中创建对应模型类型的对象,调用mapping方法来转换数据,再把模型对象传出去即可。")])]),s._v(" "),t("p",[s._v("那我们在"),t("code",[s._v("Modelable")]),s._v("中声明一个init方法,并传入一个参数,区别于其它初始化方法")]),s._v(" "),t("div",{staticClass:"language-swift extra-class"},[t("pre",{pre:!0,attrs:{class:"language-swift"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("protocol")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Modelable")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("mutating")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("func")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function-definition function"}},[s._v("mapping")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token omit keyword"}},[s._v("_")]),s._v(" json"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token constant"}},[s._v("JSON")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("init")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token omit keyword"}},[s._v("_")]),s._v(" json"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token constant"}},[s._v("JSON")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])])]),t("p",[s._v("OK,现在把"),t("code",[s._v("mapObject")]),s._v("方法补齐模型解析过程")]),s._v(" "),t("div",{staticClass:"language-swift extra-class"},[t("pre",{pre:!0,attrs:{class:"language-swift"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("func")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function-definition function"}},[s._v("mapObject")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("T")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Modelable")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token omit keyword"}},[s._v("_")]),s._v(" type"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("T")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("Type")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("->")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("T")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("let")]),s._v(" modelJson "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("JSON")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("data"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token string-literal"}},[t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"modelKey"')])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 模型解析过程")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("var")]),s._v(" obj "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("T")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("init")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("modelJson"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n obj"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("mapping")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("modelJson"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" obj\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])])]),t("h3",{attrs:{id:"_3、自定义解析键名"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3、自定义解析键名"}},[s._v("#")]),s._v(" 3、自定义解析键名")]),s._v(" "),t("p",[s._v("Q: 这样是搞定解析了,但是网络请求回来的json格式错综复杂,有什么办法可以让开发者来自行指定model对应的键名呢?")]),s._v(" "),t("p",[s._v("A: "),t("strong",[s._v("嗯嗯,既然解析过程是在 Response 扩展里操作的,那我们可以通过协议定义键名属性,并且使用 Runtime 给Response动态添加一个属性,来记录遵守协议后的相应类名")])]),s._v(" "),t("div",{staticClass:"language-swift extra-class"},[t("pre",{pre:!0,attrs:{class:"language-swift"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("protocol")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ModelableParameterType")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("/// 请求成功时状态码对应的值")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("static")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("var")]),s._v(" successValue"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("get")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("/// 状态码对应的键")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("static")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("var")]),s._v(" statusCodeKey"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("get")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("/// 请求后的提示语对应的键")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("static")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("var")]),s._v(" tipStrKey"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("get")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("/// 请求后的主要模型数据的键")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("static")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("var")]),s._v(" modelKey"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("get")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])])]),t("div",{staticClass:"language-swift extra-class"},[t("pre",{pre:!0,attrs:{class:"language-swift"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// MARK:- runtime")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("extension")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Response")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("private")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("struct")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("AssociatedKeys")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("static")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("var")]),s._v(" lxf_modelableParameterKey "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string-literal"}},[t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"lxf_modelableParameterKey"')])]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("var")]),s._v(" lxf_modelableParameter"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ModelableParameterType")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("Type")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("get")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("let")]),s._v(" value "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("objc_getAssociatedObject")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("self")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("&")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("AssociatedKeys")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("lxf_modelableParameterKey"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("as")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("AnyObject")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("guard")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("let")]),s._v(" type "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" value "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("as")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("?")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ModelableParameterType")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("Type")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("else")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("NullParameter")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("self")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" type\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("set")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("objc_setAssociatedObject")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("self")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("&")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("AssociatedKeys")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("lxf_modelableParameterKey"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" newValue"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token constant"}},[s._v("OBJC_ASSOCIATION_RETAIN_NONATOMIC")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])])]),t("blockquote",[t("p",[t("strong",[s._v("这里有个坑:_SwiftValue问题")]),s._v(" (献上 "),t("a",{attrs:{href:"https://stackoverflow.com/questions/42033735/failing-cast-in-swift-from-any-to-protocol/42034523#42034523",target:"_blank",rel:"noopener noreferrer"}},[s._v("参考链接"),t("OutboundLink")],1),s._v(")\n如果我们存储的不是OC对象,那么"),t("code",[s._v("objc_getAssociatedObject")]),s._v("取出来的值的类型统统为"),t("code",[s._v("_SwiftValue")]),s._v(",直接"),t("code",[s._v("as? ModelableParameterType.Type")]),s._v("绝对是nil,需要在取出来后"),t("code",[s._v("as AnyObject")]),s._v("再转换为其它类型才会成功~~")])]),s._v(" "),t("p",[s._v("现在开发者就可以创建一个类来遵守"),t("code",[s._v("ModelableParameterType")]),s._v("协议,并自定义解析键名")]),s._v(" "),t("div",{staticClass:"language-swift extra-class"},[t("pre",{pre:!0,attrs:{class:"language-swift"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("struct")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("NetParameter")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ModelableParameterType")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("static")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("var")]),s._v(" successValue"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string-literal"}},[t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"false"')])]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("static")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("var")]),s._v(" statusCodeKey"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string-literal"}},[t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"error"')])]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("static")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("var")]),s._v(" tipStrKey"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string-literal"}},[t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"errMsg"')])]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("static")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("var")]),s._v(" modelKey"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string-literal"}},[t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"results"')])]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])])]),t("h3",{attrs:{id:"_4、插件注入"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_4、插件注入"}},[s._v("#")]),s._v(" 4、插件注入")]),s._v(" "),t("p",[s._v("Q: 厉害了,不过要在什么时机下存储这个自定义键名的"),t("code",[s._v("NetParameter")]),s._v("?")]),s._v(" "),t("p",[s._v("A: 额,这个~~~ 哦,对了,可以通过Moya提供的插件机制!")]),s._v(" "),t("p",[s._v("翻出Moya中的Plugin.Swift,找到这个"),t("code",[s._v("process")]),s._v("方法,看看方法说明。")]),s._v(" "),t("div",{staticClass:"language-swift extra-class"},[t("pre",{pre:!0,attrs:{class:"language-swift"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("/// 在结束之前,可以被用来修改请求结果")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("/// Called to modify a result before completion.")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("func")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function-definition function"}},[s._v("process")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token omit keyword"}},[s._v("_")]),s._v(" result"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Result")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Moya")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Response")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("MoyaError")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" target"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("TargetType")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("->")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Result")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Moya")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Response")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("MoyaError")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n")])])]),t("p",[s._v("那好,我们也做一个插件"),t("code",[s._v("MoyaMapperPlugin")]),s._v("给开发者使用,在创建"),t("code",[s._v("MoyaMapperPlugin")]),s._v("时把自定义解析键名的类型传进来")]),s._v(" "),t("div",{staticClass:"language-swift extra-class"},[t("pre",{pre:!0,attrs:{class:"language-swift"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("struct")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("MoyaMapperPlugin")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("PluginType")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("var")]),s._v(" parameter"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ModelableParameterType")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("Type")]),s._v("\n \n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("init")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("T")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ModelableParameterType")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token omit keyword"}},[s._v("_")]),s._v(" type"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("T")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("Type")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n parameter "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" type\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n \n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// modify response")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("func")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function-definition function"}},[s._v("process")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token omit keyword"}},[s._v("_")]),s._v(" result"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Result")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Response")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("MoyaError")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" target"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("TargetType")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("->")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Result")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Response")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("MoyaError")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token omit keyword"}},[s._v("_")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" result"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("map "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("response"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("->")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Response")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("in")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 趁机添加相关数据 ")]),s._v("\n response"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("lxf_modelableParameter "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" parameter\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" response\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" result\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])])]),t("p",[s._v("使用:开发者在创建"),t("code",[s._v("MoyaProvider")]),s._v("对象时,顺便注入插件。(OS: 这一步堪称“注入灵魂”)")]),s._v(" "),t("div",{staticClass:"language-swift extra-class"},[t("pre",{pre:!0,attrs:{class:"language-swift"}},[t("code",[t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("MoyaProvider")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("LXFNetworkTool")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("plugins"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("MoyaMapperPlugin")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("NetParameter")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("self")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])])]),t("h3",{attrs:{id:"_5、总结"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_5、总结"}},[s._v("#")]),s._v(" 5、总结")]),s._v(" "),t("blockquote",[t("p",[s._v("以上就是主要的踩坑过程了。模型数组解析和指定解析也跟这些差不多的,这里就不再赘述。本人已经将其封装成一个开源库 "),t("a",{attrs:{href:"https://github.com/LinXunFeng/MoyaMapper",target:"_blank",rel:"noopener noreferrer"}},[s._v("MoyaMapper"),t("OutboundLink")],1),s._v(",包含了上述已经和未曾说明的功能,下面会讲解如何去使用。以上部分可以称为开胃菜,目的就是平滑过渡到下面MoyaMapper的具体使用。")])]),s._v(" "),t("p",[s._v("可能单单使用"),t("code",[s._v("MoyaMapper")]),s._v("的默认子库"),t("code",[s._v("Core")]),s._v(",作用体会上并不会很深。但是,如果你也是使用RxSwift来开发项目的话,请安装"),t("code",[s._v("'MoyaMapper/Rx'")]),s._v("吧,绝对一个字:「爽」")]),s._v(" "),t("h2",{attrs:{id:"二、moyamapper的使用"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#二、moyamapper的使用"}},[s._v("#")]),s._v(" 二、MoyaMapper的使用")]),s._v(" "),t("p",[t("img",{attrs:{src:"/images/2018/05/%E6%89%93%E9%80%A0Moya%E4%BE%BF%E6%8D%B7%E8%A7%A3%E6%9E%90%E5%BA%93%EF%BC%8C%E6%8F%90%E4%BE%9BRxSwift%E6%8B%93%E5%B1%95/MoyaMapper.png",alt:"MoyaMapper"}})]),s._v(" "),t("p",[s._v("MoyaMapper是基于Moya和SwiftyJSON封装的工具,以Moya的plugin的方式来实现间接解析,支持RxSwift")]),s._v(" "),t("p",[t("img",{attrs:{src:"/images/2018/05/%E6%89%93%E9%80%A0Moya%E4%BE%BF%E6%8D%B7%E8%A7%A3%E6%9E%90%E5%BA%93%EF%BC%8C%E6%8F%90%E4%BE%9BRxSwift%E6%8B%93%E5%B1%95/JSON%E6%95%B0%E6%8D%AE%E5%AF%B9%E7%85%A7.png",alt:"JSON数据对照"}})]),s._v(" "),t("h3",{attrs:{id:"_1、定义并注入自定义键名类"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1、定义并注入自定义键名类"}},[s._v("#")]),s._v(" 1、定义并注入自定义键名类")]),s._v(" "),t("ol",[t("li",[s._v("定义一个遵守ModelableParameterType协议的结构体")])]),s._v(" "),t("div",{staticClass:"language-swift extra-class"},[t("pre",{pre:!0,attrs:{class:"language-swift"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 各参数返回的内容请参考上面JSON数据对照图")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("struct")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("NetParameter")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ModelableParameterType")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("static")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("var")]),s._v(" successValue"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string-literal"}},[t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"false"')])]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("static")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("var")]),s._v(" statusCodeKey"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string-literal"}},[t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"error"')])]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("static")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("var")]),s._v(" tipStrKey"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string-literal"}},[t("span",{pre:!0,attrs:{class:"token string"}},[s._v('""')])]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("static")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("var")]),s._v(" modelKey"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string-literal"}},[t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"results"')])]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])])]),t("p",[t("strong",[s._v("此外,这里还可以做简单的路径处理,以应付各种情况,以'>'隔开")])]),s._v(" "),t("div",{staticClass:"language-json extra-class"},[t("pre",{pre:!0,attrs:{class:"language-json"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 假设返回的json数据关于请求状态的相关数据如下所示,")]),s._v("\nerror"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n 'errorStatus'"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),t("span",{pre:!0,attrs:{class:"token boolean"}},[s._v("false")]),s._v("\n 'errMsg'"),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(":")]),s._v("'error Argument type'\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n")])])]),t("div",{staticClass:"language-swift extra-class"},[t("pre",{pre:!0,attrs:{class:"language-swift"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 我们指明解析路径:error对象下的errMsg字段,一层层表示下去即可")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("static")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("var")]),s._v(" tipStrKey"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("return")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string-literal"}},[t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"error>errMsg"')])]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n")])])]),t("ol",{attrs:{start:"2"}},[t("li",[s._v("以plugin的方式传递给MoyaProvider")])]),s._v(" "),t("div",{staticClass:"language-swift extra-class"},[t("pre",{pre:!0,attrs:{class:"language-swift"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// MoyaMapperPlugin这里只需要传入类型")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("MoyaProvider")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("LXFNetworkTool")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("plugins"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("MoyaMapperPlugin")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("NetParameter")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("self")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])])]),t("h3",{attrs:{id:"_2、定义解析模型"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2、定义解析模型"}},[s._v("#")]),s._v(" 2、定义解析模型")]),s._v(" "),t("p",[s._v("创建一个遵守Modelable协议的结构体")]),s._v(" "),t("div",{staticClass:"language-swift extra-class"},[t("pre",{pre:!0,attrs:{class:"language-swift"}},[t("code",[t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("struct")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("MyModel")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Modelable")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n \n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("var")]),s._v(" _id "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string-literal"}},[t("span",{pre:!0,attrs:{class:"token string"}},[s._v('""')])]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("...")]),s._v("\n \n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("init")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token omit keyword"}},[s._v("_")]),s._v(" json"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token constant"}},[s._v("JSON")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n \n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("mutating")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("func")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function-definition function"}},[s._v("mapping")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token omit keyword"}},[s._v("_")]),s._v(" json"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token constant"}},[s._v("JSON")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("self")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("_id "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" json"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token string-literal"}},[t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"_id"')])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),s._v("stringValue\n "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("...")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n\n遵守"),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Modelable")]),s._v("协议,实现协议的两个方法,在`mapping`方法中描述模型字段的具体解析\n\n")])])]),t("h3",{attrs:{id:"_3、解析数据"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3、解析数据"}},[s._v("#")]),s._v(" 3、解析数据")]),s._v(" "),t("h4",{attrs:{id:"_0x00-请求结果与模型解析"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_0x00-请求结果与模型解析"}},[s._v("#")]),s._v(" 0x00 请求结果与模型解析")]),s._v(" "),t("div",{staticClass:"language-swift extra-class"},[t("pre",{pre:!0,attrs:{class:"language-swift"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// Result")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("func")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function-definition function"}},[s._v("mapResult")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("params"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ModelableParamsBlock")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("?")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token nil constant"}},[s._v("nil")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("->")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("MoyaMapperResult")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// Model")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("func")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function-definition function"}},[s._v("mapObject")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("T")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Modelable")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token omit keyword"}},[s._v("_")]),s._v(" type"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("T")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("Type")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" modelKey"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("?")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token nil constant"}},[s._v("nil")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("->")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("T")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// Result+Model")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("func")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function-definition function"}},[s._v("mapObjResult")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("T")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Modelable")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token omit keyword"}},[s._v("_")]),s._v(" type"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("T")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("Type")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" params"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ModelableParamsBlock")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("?")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token nil constant"}},[s._v("nil")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("->")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("MoyaMapperResult")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("T")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// Models")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("func")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function-definition function"}},[s._v("mapArray")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("T")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Modelable")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token omit keyword"}},[s._v("_")]),s._v(" type"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("T")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("Type")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" modelKey"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("?")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token nil constant"}},[s._v("nil")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("->")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("T")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// Result+Models")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("func")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function-definition function"}},[s._v("mapArrayResult")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("T")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Modelable")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token omit keyword"}},[s._v("_")]),s._v(" type"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("T")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(".")]),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("Type")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" params"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("ModelableParamsBlock")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("?")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token nil constant"}},[s._v("nil")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("->")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("MoyaMapperResult")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("T")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])])]),t("p",[s._v("上面的五个方法,观其名,知其意,这里就不过多解释了,主要注意两点:")]),s._v(" "),t("ul",[t("li",[s._v("result")])]),s._v(" "),t("div",{staticClass:"language-swift extra-class"},[t("pre",{pre:!0,attrs:{class:"language-swift"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 元祖类型")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 参数1:根据statusCodeKey取出的值与successValue是否相等")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 参数2:根据tipStrKey取出的值")]),s._v("\nresult:"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Bool")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])])]),t("ul",[t("li",[s._v("params")])]),s._v(" "),t("div",{staticClass:"language-swift extra-class"},[t("pre",{pre:!0,attrs:{class:"language-swift"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// params: ModelableParamsBlock? = nil")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 这里只有在特殊场景下才需要使用到。如:项目中需要在某处使用特定接口,但是返回的json格式跟自己项目的不一样,并且只有这么一两处用得着该额外接口,那就需要我们这个参数了,以Block的方式返回解析参数类型。")]),s._v("\n")])])]),t("h4",{attrs:{id:"_0x01、特定解析"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_0x01、特定解析"}},[s._v("#")]),s._v(" 0x01、特定解析")]),s._v(" "),t("div",{staticClass:"language-swift extra-class"},[t("pre",{pre:!0,attrs:{class:"language-swift"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// Model")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("func")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function-definition function"}},[s._v("toJSON")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("modelKey"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("?")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token nil constant"}},[s._v("nil")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("->")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token constant"}},[s._v("JSON")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// 获取指定路径的值")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("public")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("func")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token function-definition function"}},[s._v("fetchJSONString")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("path"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("?")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token nil constant"}},[s._v("nil")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" keys"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("JSONSubscriptType")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("->")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v("\n")])])]),t("p",[s._v("这两个方法,如果没有指定路径,默认都是针对modelKey的")]),s._v(" "),t("div",{staticClass:"language-swift extra-class"},[t("pre",{pre:!0,attrs:{class:"language-swift"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// fetchJSONString(keys: <[JSONSubscriptType]>)")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("1")]),s._v("、通过 keys 传递数组"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(",")]),s._v(" 该数组可传入的类型为 "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("Int")]),s._v(" 和 "),t("span",{pre:!0,attrs:{class:"token class-name"}},[s._v("String")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2")]),s._v("、默认是以 modelKey 所示路径,来获取相应的数值。如果modelKey并非是你所想要使用的解析路径,可以使用下方的重载方法重新指定路径即可\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("// response.fetchJSONString(path: , keys: <[JSONSubscriptType]>)")]),s._v("\n")])])]),t("p",[t("strong",[s._v("MoyaMapper也提供了Rx子库,为方便RxSwift的流式编程下便捷解析数据")])]),s._v(" "),t("div",{staticClass:"language-ruby extra-class"},[t("pre",{pre:!0,attrs:{class:"language-ruby"}},[t("code",[s._v("MoyaMapper默认只安装Core下的文件\npod "),t("span",{pre:!0,attrs:{class:"token string-literal"}},[t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'MoyaMapper'")])]),s._v("\n\nRxSwift拓展\npod "),t("span",{pre:!0,attrs:{class:"token string-literal"}},[t("span",{pre:!0,attrs:{class:"token string"}},[s._v("'MoyaMapper/Rx'")])]),s._v("\n")])])]),t("p",[s._v("具体使用还不是很明白的同学可以下载并运行"),t("code",[s._v("Example")]),s._v("看看")]),s._v(" "),t("p",[t("strong",[s._v("如果"),t("a",{attrs:{href:"https://github.com/LinXunFeng/MoyaMapper",target:"_blank",rel:"noopener noreferrer"}},[s._v("MoyaMapper"),t("OutboundLink")],1),s._v("有什么不足的地方,欢迎提出issues,感谢大家的支持")])])])}),[],!1,null,null,null);t.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/105.f584d8aa.js b/assets/js/105.f584d8aa.js new file mode 100644 index 000000000..cde2fa599 --- /dev/null +++ b/assets/js/105.f584d8aa.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[105],{417:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("blockquote",[s("p",[t._v("关于使用面向协议来封装功能的实战可以参考我上篇文章 "),s("RouterLink",{attrs:{to:"/2018/04/07/iOS-面向协议方式封装空白页功能/"}},[t._v("【iOS-面向协议方式封装空白页功能】")]),t._v(",这里就不再赘述,我们直接进入使用阶段吧。\n本篇文章只有一个目的,那就是只要遵守协议,一行代码随意切换全屏~")],1)]),t._v(" "),s("p",[t._v("如果对面向协议有疑问的同学可以看下我之前的两篇文章")]),t._v(" "),s("p",[s("RouterLink",{attrs:{to:"/pages/997f68/"}},[t._v("iOS - Swift 面向协议编程(一)")])],1),t._v(" "),s("p",[s("RouterLink",{attrs:{to:"/pages/fb0480/"}},[t._v("iOS - Swift 面向协议编程(二)")])],1),t._v(" "),s("h2",{attrs:{id:"开源库"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#开源库"}},[t._v("#")]),t._v(" 开源库")]),t._v(" "),s("table",[s("thead",[s("tr",[s("th",[t._v("Name")]),t._v(" "),s("th",[t._v("Link")])])]),t._v(" "),s("tbody",[s("tr",[s("td",[t._v("GitHub")]),t._v(" "),s("td",[s("a",{attrs:{href:"https://github.com/LinXunFeng/LXFProtocolTool",target:"_blank",rel:"noopener noreferrer"}},[t._v("LXFProtocolTool"),s("OutboundLink")],1)])]),t._v(" "),s("tr",[s("td",[t._v("Wiki")]),t._v(" "),s("td",[s("a",{attrs:{href:"https://github.com/LinXunFeng/LXFProtocolTool/wiki",target:"_blank",rel:"noopener noreferrer"}},[t._v("Wiki首页"),s("OutboundLink")],1)])]),t._v(" "),s("tr",[s("td",[t._v("本文 Demo")]),t._v(" "),s("td",[s("a",{attrs:{href:"https://github.com/LinXunFeng/LXFProtocolTool/tree/master/Example/LXFProtocolTool/Demo/LXFFullScreenable",target:"_blank",rel:"noopener noreferrer"}},[t._v("LXFFullScreenable"),s("OutboundLink")],1)])])])]),t._v(" "),s("p",[t._v("使用Cocoapods的方式来安装即可")]),t._v(" "),s("div",{staticClass:"language-ruby extra-class"},[s("pre",{pre:!0,attrs:{class:"language-ruby"}},[s("code",[t._v("pod "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'LXFProtocolTool/FullScreenable'")])]),t._v("\n")])])]),s("h2",{attrs:{id:"一、配置"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#一、配置"}},[t._v("#")]),t._v(" 一、配置")]),t._v(" "),s("p",[t._v("在AppDelegate中实现如下方法")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("application")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token omit keyword"}},[t._v("_")]),t._v(" application"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIApplication")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" supportedInterfaceOrientationsFor window"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIWindow")]),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 operator"}},[t._v("->")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIInterfaceOrientationMask")]),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 class-name"}},[t._v("UIApplication")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("shared"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("lxf"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("currentVcOrientationMask\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("h2",{attrs:{id:"二、使用案例"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#二、使用案例"}},[t._v("#")]),t._v(" 二、使用案例")]),t._v(" "),s("blockquote",[s("p",[t._v("方法与属性的调用都需要命名空间加上 "),s("code",[t._v("lxf")]),t._v(",如"),s("code",[t._v("isFullScreen")]),t._v(" -> "),s("code",[t._v("lxf.isFullScreen")])])]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[t._v("isFullScreen "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 获取当前遵守协议者是否为全屏状态\n")])])]),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("switchFullScreen")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n isEnter"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Bool")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n specifiedView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIView")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n superView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIView")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n config"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FullScreenableConfig")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n completed"),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 omit keyword"}},[t._v("_")]),t._v(" isFullScreen"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Bool")]),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 class-name"}},[t._v("Void")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("table",[s("thead",[s("tr",[s("th",[t._v("Name")]),t._v(" "),s("th",[t._v("Type")]),t._v(" "),s("th",[t._v("Desc")])])]),t._v(" "),s("tbody",[s("tr",[s("td",[t._v("isEnter")]),t._v(" "),s("td",[s("code",[t._v("Bool?")])]),t._v(" "),s("td",[t._v("是否进入全屏")])]),t._v(" "),s("tr",[s("td",[t._v("specifiedView")]),t._v(" "),s("td",[s("code",[t._v("UIView?")])]),t._v(" "),s("td",[t._v("指定即将全屏的视图")])]),t._v(" "),s("tr",[s("td",[t._v("superView")]),t._v(" "),s("td",[s("code",[t._v("UIView?")])]),t._v(" "),s("td",[t._v("作为退出全屏后specifiedView的父视图")])]),t._v(" "),s("tr",[s("td",[t._v("config")]),t._v(" "),s("td",[s("code",[t._v("FullScreenableConfig?")])]),t._v(" "),s("td",[t._v("配置")])]),t._v(" "),s("tr",[s("td",[t._v("completed")]),t._v(" "),s("td",[s("code",[t._v("((_ isFullScreen: Bool)->Void)?")])]),t._v(" "),s("td",[t._v("进入/退出 全屏后的回调")])])])]),t._v(" "),s("blockquote",[s("p",[t._v("当"),s("code",[t._v("switchFullScreen")]),t._v("的调用者为"),s("code",[t._v("UIView")]),t._v("时,如果"),s("code",[t._v("specifiedView")]),t._v("为"),s("code",[t._v("nil")]),t._v("会自动填写,"),s("code",[t._v("superView")]),t._v("也是如此")])]),t._v(" "),s("blockquote",[s("p",[s("code",[t._v("switchFullScreen")]),t._v("方法不推荐直接使用,不过当遵守协议者为"),s("code",[t._v("UIViewController")]),t._v("时,可以通过使用默认参数来切换屏幕方向"),s("code",[t._v("lxf.switchFullScreen()")])])]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110220132.gif",alt:"lxf_FullScreenable_1"}})]),t._v(" "),s("p",[t._v("以下分两种情况说明")]),t._v(" "),s("h3",{attrs:{id:"uiviewcontroller"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#uiviewcontroller"}},[t._v("#")]),t._v(" UIViewController")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("enterFullScreen")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n specifiedView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n config"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FullScreenableConfig")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n completed"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FullScreenableCompleteType")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("exitFullScreen")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n superView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n config"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FullScreenableConfig")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n completed"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FullScreenableCompleteType")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),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("switchFullScreen")]),t._v("的抽离,使调用时对参数的传递更加清晰")])]),t._v(" "),s("p",[t._v("1、遵守协议 "),s("code",[t._v("FullScreenable")])]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[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("LXFFullScreenableController")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIViewController")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FullScreenable")]),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("p",[t._v("2、指定视图进入全屏")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[t._v("lxf"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("enterFullScreen")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("specifiedView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" cyanView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("3、指定视图退出全屏,并添加到当前控制器的"),s("code",[t._v("view")]),t._v("上")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[t._v("lxf"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("exitFullScreen")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("superView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("view"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("h4",{attrs:{id:"🔥自动进入-退出全屏"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#🔥自动进入-退出全屏"}},[t._v("#")]),t._v(" 🔥自动进入|退出全屏")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("autoFullScreen")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n specifiedView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n superView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n config"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FullScreenableConfig")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n")])])]),s("ul",[s("li",[t._v("控制器可以调用该方法来注册自动进入或退出全屏,各控制器之间互不影响。")]),t._v(" "),s("li",[s("code",[t._v("view")]),t._v("手动进入全屏会屏蔽当前控制器的自动全屏功能,退出方可恢复")])]),t._v(" "),s("h3",{attrs:{id:"uiview"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#uiview"}},[t._v("#")]),t._v(" UIView")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("enterFullScreen")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n specifiedView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIView")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n config"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FullScreenableConfig")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n completed"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FullScreenableCompleteType")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("exitFullScreen")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n superView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIView")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n config"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FullScreenableConfig")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n completed"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FullScreenableCompleteType")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),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("switchFullScreen")]),t._v("的抽离,使调用时对参数的传递更加清晰")])]),t._v(" "),s("p",[t._v("1、遵守协议 "),s("code",[t._v("FullScreenable")])]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[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("LXFFullScreenView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIButton")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FullScreenable")]),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("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" cyanView "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFFullScreenView")]),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("2、进入全屏")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[t._v("cyanView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("lxf"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("enterFullScreen")]),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("3、退出全屏")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[t._v("cyanView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("lxf"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("exitFullScreen")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("blockquote",[s("p",[t._v("这里是对遵守了"),s("code",[t._v("FullScreenable")]),t._v("协议的视图进入全屏切换,由于代码内部已经经过自动视图填写,所以\b直接调用相应的方法即可,当然也可以自己指定"),s("code",[t._v("specifiedView")]),t._v("和"),s("code",[t._v("superView")])])]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110220150.gif",alt:"lxf_FullScreenable_2"}})]),t._v(" "),s("h2",{attrs:{id:"三、fullscreenableconfig说明"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#三、fullscreenableconfig说明"}},[t._v("#")]),t._v(" 三、FullScreenableConfig说明")]),t._v(" "),s("blockquote",[s("p",[t._v("上述的方法都有一个"),s("code",[t._v("config")]),t._v("参数,默认为nil,即为默认配置")])]),t._v(" "),s("p",[t._v("相关属性说明")]),t._v(" "),s("table",[s("thead",[s("tr",[s("th",[t._v("Name")]),t._v(" "),s("th",[t._v("Type")]),t._v(" "),s("th",[t._v("Desc")]),t._v(" "),s("th",[t._v("Default")])])]),t._v(" "),s("tbody",[s("tr",[s("td",[t._v("animateDuration")]),t._v(" "),s("td",[s("code",[t._v("Double")])]),t._v(" "),s("td",[t._v("进入/退出 全屏时的旋转动画时间")]),t._v(" "),s("td",[t._v("0.25")])]),t._v(" "),s("tr",[s("td",[t._v("enterFullScreenOrientation")]),t._v(" "),s("td",[s("code",[t._v("UIInterfaceOrientation")])]),t._v(" "),s("td",[t._v("进入全屏时的初始方向")]),t._v(" "),s("td",[t._v("landscapeRight")])])])]),t._v(" "),s("p",[t._v("这里我们把动画时间设置为"),s("code",[t._v("1s")]),t._v(",初始方向为"),s("code",[t._v("左")]),t._v("后来看看效果")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FullScreenableConfig")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n animateDuration"),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 enterFullScreenOrientation "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("landscapeLeft\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[t._v("cyanView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("lxf"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("enterFullScreen")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("config"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" diyConfig"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\ncyanView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("lxf"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("exitFullScreen")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("config"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" diyConfig"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110220202.gif",alt:"lxf_FullScreenable_3"}})]),t._v(" "),s("h2",{attrs:{id:"结语"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#结语"}},[t._v("#")]),t._v(" 结语")]),t._v(" "),s("p",[t._v("到这里相关的说明已罗列完毕,有什么不清楚的可以下载"),s("a",{attrs:{href:"https://github.com/LinXunFeng/LXFProtocolTool/tree/master/Example/LXFProtocolTool/Demo/LXFFullScreenable",target:"_blank",rel:"noopener noreferrer"}},[t._v("Demo"),s("OutboundLink")],1),t._v("看看,或者在文章下方留言提问")]),t._v(" "),s("p",[s("a",{attrs:{href:"https://github.com/LinXunFeng/LXFProtocolTool",target:"_blank",rel:"noopener noreferrer"}},[t._v("LXFProtocolTool"),s("OutboundLink")],1),t._v(" 主要是通过协议的方式来方便快捷地实现一些的实用功能,除了本文提及的全屏旋转功能外还有其它实用功能的封装,具体内容可以到 "),s("a",{attrs:{href:"https://github.com/LinXunFeng/LXFProtocolTool/wiki",target:"_blank",rel:"noopener noreferrer"}},[t._v("Wiki首页"),s("OutboundLink")],1),t._v(" 查找。如果你有什么想实现的功能也可以提出来,喜欢的就给个Star鼓励下我吧 🚀 🚀 🚀,感谢支持!")])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/106.9459ada7.js b/assets/js/106.9459ada7.js new file mode 100644 index 000000000..4e03f8c5d --- /dev/null +++ b/assets/js/106.9459ada7.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[106],{418:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("blockquote",[s("p",[t._v("在日常开发中常常会对设备进行一定的适配,为了方便在多个项目里统一管理和使用,所以封装并开源了"),s("code",[t._v("SwiftyFitsize")]),t._v("这个库,可用于适配视图及字体大小,同时也支持 xib 和 storyboard")])]),t._v(" "),s("p",[t._v("GitHub: "),s("a",{attrs:{href:"https://github.com/LinXunFeng/SwiftyFitsize",target:"_blank",rel:"noopener noreferrer"}},[t._v("SwiftyFitsize"),s("OutboundLink")],1)]),t._v(" "),s("p",[t._v("最终的效果如下图所示")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110220254.png",alt:"效果图"}})]),t._v(" "),s("h2",{attrs:{id:"安装"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#安装"}},[t._v("#")]),t._v(" 安装")]),t._v(" "),s("p",[t._v("使用Cocoapods安装,或手动拖入项目")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("pod 'SwiftyFitsize'\n")])])]),s("h2",{attrs:{id:"使用"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用"}},[t._v("#")]),t._v(" 使用")]),t._v(" "),s("p",[s("code",[t._v("SwiftyFitsize")]),t._v("在默认状况下所使用的参照宽度为"),s("code",[t._v("iphone6")]),t._v("的"),s("code",[t._v("375")]),t._v("\n如果设计图所选用设备的宽度与默认值不同,可以在"),s("code",[t._v("AppDelegate")]),t._v("下初始化所参照的宽度")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SwiftyFitsize")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("reference")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("width"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("414")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("下面列出一些设备对应的分辨率,方便查找")]),t._v(" "),s("table",[s("thead",[s("tr",[s("th",[t._v("设备")]),t._v(" "),s("th",[t._v("逻辑分辨率(point)")]),t._v(" "),s("th",[t._v("设备分辨率(pixel)")])])]),t._v(" "),s("tbody",[s("tr",[s("td",[t._v("SE")]),t._v(" "),s("td",[t._v("320x568")]),t._v(" "),s("td",[t._v("640x1136")])]),t._v(" "),s("tr",[s("td",[t._v("6(S)/7/8")]),t._v(" "),s("td",[t._v("375x667")]),t._v(" "),s("td",[t._v("750x1334")])]),t._v(" "),s("tr",[s("td",[t._v("6(S)+/7+/8+")]),t._v(" "),s("td",[t._v("414x736")]),t._v(" "),s("td",[t._v("1080x1920")])]),t._v(" "),s("tr",[s("td",[t._v("X(S)")]),t._v(" "),s("td",[t._v("375x812")]),t._v(" "),s("td",[t._v("1125x2436")])]),t._v(" "),s("tr",[s("td",[t._v("XR")]),t._v(" "),s("td",[t._v("414x896")]),t._v(" "),s("td",[t._v("828x1792")])]),t._v(" "),s("tr",[s("td",[t._v("XS Max")]),t._v(" "),s("td",[t._v("414x896")]),t._v(" "),s("td",[t._v("1242x2688")])])])]),t._v(" "),s("p",[t._v("使用也是非常方便的,只需要在"),s("code",[t._v("Number")]),t._v("、"),s("code",[t._v("UIFont")]),t._v("、"),s("code",[t._v("CGPoint")]),t._v("、"),s("code",[t._v("CGSize")]),t._v("、"),s("code",[t._v("UIEdgeInsetsMake")]),t._v("这些类型的值后面加上"),s("code",[t._v("~")]),t._v("即可")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token number"}},[t._v("100")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("~")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIFont")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("systemFont")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ofSize"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("14")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("~")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CGPoint")]),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 number"}},[t._v("10")]),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(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("~")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CGSize")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("width"),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(" 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(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("~")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CGRect")]),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 number"}},[t._v("10")]),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(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" width"),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(" 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(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("~")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIEdgeInsetsMake")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("~")]),t._v("\n")])])]),s("h5",{attrs:{id:"xib-storyboard-字体适配"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#xib-storyboard-字体适配"}},[t._v("#")]),t._v(" xib / storyboard 字体适配")]),t._v(" "),s("p",[t._v("支持控件 "),s("code",[t._v("UILabel")]),t._v(" "),s("code",[t._v("UIButton")]),t._v(" "),s("code",[t._v("UITextView")]),t._v(" "),s("code",[t._v("UITextField")])]),t._v(" "),s("p",[s("img",{attrs:{src:"https://github.com/LinXunFeng/SwiftyFitsize/raw/master/Screenshots/xib-font.png",alt:"xib-font"}})]),t._v(" "),s("h5",{attrs:{id:"xib-storyboard-约束适配"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#xib-storyboard-约束适配"}},[t._v("#")]),t._v(" xib / storyboard 约束适配")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://github.com/LinXunFeng/SwiftyFitsize/raw/master/Screenshots/xib-constraint.png",alt:"xib-font"}})]),t._v(" "),s("h5",{attrs:{id:""}},[s("a",{staticClass:"header-anchor",attrs:{href:"#"}},[t._v("#")])]),t._v(" "),s("p",[t._v("注:"),s("code",[t._v("~")]),t._v("请不要相互嵌套使用,如")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CGPoint")]),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 number"}},[t._v("10")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("~")]),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(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),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 operator"}},[t._v("~")]),t._v("\n")])])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/107.c6735fa5.js b/assets/js/107.c6735fa5.js new file mode 100644 index 000000000..1b2c69630 --- /dev/null +++ b/assets/js/107.c6735fa5.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[107],{420:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("ul",[s("li",[s("code",[t._v("Moya")]),t._v(" 在Swift开发中起着重要的网络交互作用,但是还有不如之处,比如网络不可用时,返回的 "),s("code",[t._v("Response")]),t._v(" 为 "),s("code",[t._v("nil")]),t._v(",这时还得去解析相应的 "),s("code",[t._v("Error")])]),t._v(" "),s("li",[s("code",[t._v("Codable")]),t._v(" 可以帮助我们快速的解析数据,但是一旦声明的属性类型与json中的不一致,将无法正常解析; 而且对于模型中自定义属性名的处理也十分繁琐")])]),t._v(" "),s("p",[t._v("解决的方案有很多,不过我比较习惯使用 "),s("code",[t._v("MoyaMapper")]),t._v(" ,不仅可以解决上述问题,还提供了多种"),s("code",[t._v("模型转换")]),t._v("、"),s("code",[t._v("数据互转")]),t._v("、"),s("code",[t._v("多种数据类型任意存储")]),t._v("的便捷方法。掌控Moya的网络请求、数据解析与缓存简直易如反掌。")]),t._v(" "),s("blockquote",[s("p",[s("code",[t._v("MoyaMapper")]),t._v("是基于Moya和SwiftyJSON封装的工具,以Moya的plugin的方式来实现间接解析,支持RxSwift")]),t._v(" "),s("p",[t._v("GitHub: "),s("a",{attrs:{href:"https://github.com/MoyaMapper/MoyaMapper",target:"_blank",rel:"noopener noreferrer"}},[t._v("MoyaMapper"),s("OutboundLink")],1)]),t._v(" "),s("p",[t._v("📖 详细的使用请查看手册 "),s("a",{attrs:{href:"https://moyamapper.github.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://MoyaMapper.github.io"),s("OutboundLink")],1)])]),t._v(" "),s("h2",{attrs:{id:"特点"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#特点"}},[t._v("#")]),t._v(" 特点")]),t._v(" "),s("ul",[s("li",[t._v("支持"),s("code",[t._v("json")]),t._v(" 转 "),s("code",[t._v("Model")]),t._v(" 自动映射 与 自定义映射")]),t._v(" "),s("li",[t._v("无视 "),s("code",[t._v("json")]),t._v(" 中值的类型,"),s("code",[t._v("Model")]),t._v(" 中属性声明的是什么类型,它就是什么类型")]),t._v(" "),s("li",[t._v("支持 "),s("code",[t._v("Data")]),t._v(" "),s("code",[t._v("字典")]),t._v(" "),s("code",[t._v("JSON")]),t._v(" "),s("code",[t._v("json字符串")]),t._v(" "),s("code",[t._v("Model")]),t._v(" 互转")]),t._v(" "),s("li",[t._v("插件方式,全方位保障"),s("code",[t._v("Moya.Response")]),t._v(",拒绝各种网络问题导致 "),s("code",[t._v("Response")]),t._v(" 为 "),s("code",[t._v("nil")]),t._v(",将各式各样的原因导致的数据加载失败进行统一处理,开发者只需要关注 "),s("code",[t._v("Response")])]),t._v(" "),s("li",[t._v("可选 - 支持数据随意缓存( "),s("code",[t._v("JSON")]),t._v(" 、 "),s("code",[t._v("Number")]),t._v(" 、"),s("code",[t._v("String")]),t._v("、 "),s("code",[t._v("Bool")]),t._v("、 "),s("code",[t._v("Moya.Response")]),t._v(" )")]),t._v(" "),s("li",[t._v("可选 - 支持网络请求缓存")])]),t._v(" "),s("h2",{attrs:{id:"数据解析"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#数据解析"}},[t._v("#")]),t._v(" 数据解析")]),t._v(" "),s("h5",{attrs:{id:"一、插件注入"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#一、插件注入"}},[t._v("#")]),t._v(" 一、插件注入")]),t._v(" "),s("p",[t._v("附:"),s("a",{attrs:{href:"https://moyamapper.github.io/plugin/",target:"_blank",rel:"noopener noreferrer"}},[t._v("插件 MoyaMapperPlugin 的详细使用"),s("OutboundLink")],1)]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110220403.png",alt:""}})]),t._v(" "),s("p",[t._v("1、定义适用于项目接口的 "),s("code",[t._v("ModelableParameterType")])]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token comment"}},[t._v('// statusCodeKey、tipStrKey、 modelKey 可以任意指定级别的路径,如: "error>used"')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("NetParameter")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ModelableParameterType")]),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(" successValue "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"000"')])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" statusCodeKey "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"retStatus"')])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" tipStrKey "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"retMsg"')])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" modelKey "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"retBody"')])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("2、在 "),s("code",[t._v("MoyaProvider")]),t._v(" 中使用 "),s("code",[t._v("MoyaMapperPlugin")]),t._v(" 插件,并指定 "),s("code",[t._v("ModelableParameterType")])]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" lxfNetTool "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MoyaProvider")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFNetworkTool")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),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(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MoyaMapperPlugin")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("NetParameter")]),s("span",{pre:!0,attrs:{class:"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("p",[t._v("❗ 使用 "),s("code",[t._v("MoyaMapperPlugin")]),t._v(" 插件是整个 "),s("code",[t._v("MoyaMapper")]),t._v(" 的核心所在!")]),t._v(" "),s("h5",{attrs:{id:"二、model声明"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#二、model声明"}},[t._v("#")]),t._v(" 二、Model声明")]),t._v(" "),s("blockquote",[s("p",[s("code",[t._v("Model")]),t._v(" 需遵守 "),s("code",[t._v("Modelable")]),t._v(" 协议")]),t._v(" "),s("ul",[s("li",[s("code",[t._v("MoyaMapper")]),t._v(" 支持模型自动映射 和 自定义映射")]),t._v(" "),s("li",[t._v("不需要考虑源json数据的真实类型,这里统一按 "),s("code",[t._v("Model")]),t._v(" 中属性声明的类型进行转换")])])]),t._v(" "),s("p",[t._v("1、一般情况下如下写法即可")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CompanyModel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Modelable")]),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("var")]),t._v(" name "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" catchPhrase "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')])]),t._v("\n \n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("init")]),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 punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("2、如果自定义映射,则可以实现方法 "),s("code",[t._v("mutating func mapping(_ json: JSON)")])]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CompanyModel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Modelable")]),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("var")]),t._v(" name "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" catchPhrase "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')])]),t._v("\n \n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("init")]),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 punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("mutating")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("mapping")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token omit keyword"}},[t._v("_")]),t._v(" json"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token constant"}},[t._v("JSON")]),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("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" json"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"nickname"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("stringValue\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("3、支持模型嵌套")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UserModel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Modelable")]),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("var")]),t._v(" id "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" name "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" company "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CompanyModel")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CompanyModel")]),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("init")]),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 punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("h5",{attrs:{id:"三、response-解析"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#三、response-解析"}},[t._v("#")]),t._v(" 三、Response 解析")]),t._v(" "),s("blockquote",[s("p",[t._v("1、以下示例皆使用了 "),s("code",[t._v("MoyaMapperPlugin")]),t._v(" ,所以不需要指定 "),s("code",[t._v("解析路径")])]),t._v(" "),s("p",[t._v("2、如果没有使用 "),s("code",[t._v("MoyaMapperPlugin")]),t._v(" 则需要指定 "),s("code",[t._v("解析路径")]),t._v(",否则无法正常解析")]),t._v(" "),s("p",[t._v("ps: "),s("code",[t._v("解析路径")]),t._v(" 可以使用 "),s("code",[t._v("a>b")]),t._v(" 这种形式来解决多级路径的问题")])]),t._v(" "),s("p",[t._v("解析方法如下列表所示")]),t._v(" "),s("table",[s("thead",[s("tr",[s("th",{staticStyle:{"text-align":"center"}},[t._v("方法")]),t._v(" "),s("th",{staticStyle:{"text-align":"left"}},[t._v("描述 (支持RxSwift)")])])]),t._v(" "),s("tbody",[s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("toJSON")]),t._v(" "),s("td",{staticStyle:{"text-align":"left"}},[t._v("Response 转 JSON ( "),s("a",{attrs:{href:"https://moyamapper.github.io/core/toJSON/",target:"_blank",rel:"noopener noreferrer"}},[t._v("toJSON"),s("OutboundLink")],1),t._v(" "),s("a",{attrs:{href:"https://moyamapper.github.io/rx/toJSON/",target:"_blank",rel:"noopener noreferrer"}},[t._v("rx.toJSON"),s("OutboundLink")],1),t._v(")")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("fetchString")]),t._v(" "),s("td",{staticStyle:{"text-align":"left"}},[t._v("获取指定路径的字符串( "),s("a",{attrs:{href:"https://moyamapper.github.io/core/fetchString/",target:"_blank",rel:"noopener noreferrer"}},[t._v("fetchString"),s("OutboundLink")],1),t._v(" "),s("a",{attrs:{href:"https://moyamapper.github.io/rx/fetchString/",target:"_blank",rel:"noopener noreferrer"}},[t._v("rx.fetchString"),s("OutboundLink")],1),t._v(")")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("fetchJSONString")]),t._v(" "),s("td",{staticStyle:{"text-align":"left"}},[t._v("获取指定路径的原始json字符串 ( "),s("a",{attrs:{href:"https://moyamapper.github.io/core/fetchJSONString/",target:"_blank",rel:"noopener noreferrer"}},[t._v("fetchJSONString"),s("OutboundLink")],1),t._v(" "),s("a",{attrs:{href:"https://moyamapper.github.io/rx/fetchJSONString/",target:"_blank",rel:"noopener noreferrer"}},[t._v("rx.fetchJSONString"),s("OutboundLink")],1),t._v(" )")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("mapResult")]),t._v(" "),s("td",{staticStyle:{"text-align":"left"}},[t._v("Response -> MoyaMapperResult "),s("code",[t._v("(Bool, String)")]),t._v(" ( "),s("a",{attrs:{href:"https://moyamapper.github.io/core/mapResult/",target:"_blank",rel:"noopener noreferrer"}},[t._v("mapResult"),s("OutboundLink")],1),t._v(" "),s("a",{attrs:{href:"https://moyamapper.github.io/rx/mapResult/",target:"_blank",rel:"noopener noreferrer"}},[t._v("rx.mapResult"),s("OutboundLink")],1),t._v(" )")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("mapObject")]),t._v(" "),s("td",{staticStyle:{"text-align":"left"}},[t._v("Response -> Model ( "),s("a",{attrs:{href:"https://moyamapper.github.io/core/mapObject/",target:"_blank",rel:"noopener noreferrer"}},[t._v("mapObject"),s("OutboundLink")],1),t._v(" "),s("a",{attrs:{href:"https://moyamapper.github.io/rx/mapObject/",target:"_blank",rel:"noopener noreferrer"}},[t._v("rx.mapObject"),s("OutboundLink")],1),t._v(")")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("mapObjResult")]),t._v(" "),s("td",{staticStyle:{"text-align":"left"}},[t._v("Response -> (MoyaMapperResult, Model) ( "),s("a",{attrs:{href:"https://moyamapper.github.io/core/mapObjResult/",target:"_blank",rel:"noopener noreferrer"}},[t._v("mapObjResult"),s("OutboundLink")],1),t._v(" "),s("a",{attrs:{href:"https://moyamapper.github.io/rx/mapObjResult/",target:"_blank",rel:"noopener noreferrer"}},[t._v("rx.mapObjResult"),s("OutboundLink")],1),t._v(")")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("mapArray")]),t._v(" "),s("td",{staticStyle:{"text-align":"left"}},[t._v("Response -> [Model] ( "),s("a",{attrs:{href:"https://moyamapper.github.io/core/mapArray/",target:"_blank",rel:"noopener noreferrer"}},[t._v("mapArray"),s("OutboundLink")],1),t._v(" "),s("a",{attrs:{href:"https://moyamapper.github.io/rx/mapArray/",target:"_blank",rel:"noopener noreferrer"}},[t._v("rx.mapArray"),s("OutboundLink")],1),t._v(")")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("mapArrayResult")]),t._v(" "),s("td",{staticStyle:{"text-align":"left"}},[t._v("Response -> (MoyaMapperResult, [Model]) ( "),s("a",{attrs:{href:"https://moyamapper.github.io/core/mapArrayResult/",target:"_blank",rel:"noopener noreferrer"}},[t._v("mapArrayResult"),s("OutboundLink")],1),t._v(" "),s("a",{attrs:{href:"https://moyamapper.github.io/rx/mapArrayResult/",target:"_blank",rel:"noopener noreferrer"}},[t._v("rx.mapArrayResult"),s("OutboundLink")],1),t._v(")")])])])]),t._v(" "),s("p",[t._v("❗除了 "),s("code",[t._v("fetchJSONString")]),t._v(" 的默认解析路径是"),s("code",[t._v("根路径")]),t._v("之外,其它方法的默认解析路径为插件对象中的 "),s("code",[t._v("modelKey")])]),t._v(" "),s("p",[t._v("如果接口请求后 "),s("code",[t._v("json")]),t._v(" 的数据结构与下图类似,则使用 "),s("code",[t._v("MoyaMapper")]),t._v(" 是最合适不过了")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110220403.png",alt:""}})]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Normal")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" model "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" response"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("mapObject")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MMModel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"name -- ')]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[t._v("\\(")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[t._v("model"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name")]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation 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("\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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"github -- ')]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[t._v("\\(")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[t._v("model"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("github")]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation 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("\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 打印json")]),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("response"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("fetchJSONString")]),s("span",{pre:!0,attrs:{class:"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("// Rx")]),t._v("\nrxRequest"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("mapObject")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MMModel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("subscribe")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("onSuccess"),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 punctuation"}},[t._v("(")]),t._v("model"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"name -- ')]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[t._v("\\(")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[t._v("model"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name")]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation 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("\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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"github -- ')]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[t._v("\\(")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[t._v("model"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("github")]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation 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("\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("disposed")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("by"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" disposeBag"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("附: "),s("a",{attrs:{href:"https://moyamapper.github.io/core/fetchJSONString/",target:"_blank",rel:"noopener noreferrer"}},[t._v("fetchJSONString的详细使用"),s("OutboundLink")],1)]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110220434.png",alt:""}})]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Normal")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" models "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" response"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("mapArray")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MMModel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" name "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" models"),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 punctuation"}},[t._v(".")]),t._v("name\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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"count -- ')]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[t._v("\\(")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[t._v("models"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count")]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation 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("\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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"name -- ')]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[t._v("\\(")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[t._v("name")]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation 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("\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 打印 json 模型数组中第一个的name")]),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("response"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("fetchString")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("keys"),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 number"}},[t._v("0")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"name"')])]),s("span",{pre:!0,attrs:{class:"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("// Rx")]),t._v("\nrxRequest"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("mapArray")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MMModel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("subscribe")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("onSuccess"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" models "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" name "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" models"),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 punctuation"}},[t._v(".")]),t._v("name\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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"count -- ')]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[t._v("\\(")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[t._v("models"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count")]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation 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("\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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"name -- ')]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[t._v("\\(")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[t._v("name")]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation 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("\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("disposed")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("by"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" disposeBag"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("附:"),s("a",{attrs:{href:"https://moyamapper.github.io/core/mapArray/",target:"_blank",rel:"noopener noreferrer"}},[t._v("mapArray的详细使用说明"),s("OutboundLink")],1)]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110220444.png",alt:""}})]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Normal")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("isSuccess"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" tipStr"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" response"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("mapResult")]),s("span",{pre:!0,attrs:{class:"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 string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"isSuccess -- ')]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[t._v("\\(")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[t._v("isSuccess")]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation 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("\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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"tipStr -- ')]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[t._v("\\(")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[t._v("tipStr")]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation 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("\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Rx")]),t._v("\nrxRequest"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("mapResult")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 function"}},[t._v("subscribe")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("onSuccess"),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 punctuation"}},[t._v("(")]),t._v("isSuccess"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" tipStr"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"isSuccess -- ')]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[t._v("\\(")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[t._v("isSuccess")]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation 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(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v('// 是否为 "000"')]),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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"retMsg -- ')]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[t._v("\\(")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[t._v("retMsg")]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation 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(" "),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("disposed")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("by"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" disposeBag"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("附:"),s("a",{attrs:{href:"https://moyamapper.github.io/core/mapResult/",target:"_blank",rel:"noopener noreferrer"}},[t._v("mapResult的详细使用说明"),s("OutboundLink")],1)]),t._v(" "),s("h2",{attrs:{id:"统一处理网络请求结果"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#统一处理网络请求结果"}},[t._v("#")]),t._v(" 统一处理网络请求结果")]),t._v(" "),s("blockquote",[s("p",[t._v("在APP的实际使用过程中,会遇到各种各样的网络请求结果,如:服务器挂了、手机无网络,此时 "),s("code",[t._v("Moya")]),t._v(" 返回的 "),s("code",[t._v("Response")]),t._v(" 为 nil,这样我们就不得不去判断 "),s("code",[t._v("Error")]),t._v("。但是使用 "),s("code",[t._v("MoyaMapperPlugin")]),t._v(" 就可以让我们只关注 "),s("code",[t._v("Response")])])]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// MoyaMapperPlugin 的初始化方法")]),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("init")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("T")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ModelableParameterType")]),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 omit keyword"}},[t._v("_")]),t._v(" type"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("T")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n transformError"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Bool")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ntype "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ModelableParameterType")]),t._v(" 用于定义字段路径,做为全局解析数据的依据\ntransformError "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Bool")]),t._v(" 是否当网络请求失败时,自动转换请求结果,默认为 "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n")])])]),s("ul",[s("li",[t._v("当请求失败的时候,此时的 "),s("code",[t._v("result.response")]),t._v(" 为 "),s("code",[t._v("nil")]),t._v(",根据"),s("code",[t._v("transformError")]),t._v("是否为"),s("code",[t._v("true")]),t._v(" 判断是否创建一个自定义的 "),s("code",[t._v("response")]),t._v(" 并返回出去。")])]),t._v(" "),s("p",[t._v("➡ 本来可以请求到的数据内容")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110220403.png",alt:""}})]),t._v(" "),s("p",[t._v("➡ 现在关闭网络,再请求数据")]),t._v(" "),s("ul",[s("li",[s("p",[t._v("正常情况下,即不做任何不处理的时候, "),s("code",[t._v("Response")]),t._v(" 为 "),s("code",[t._v("nil")])])]),t._v(" "),s("li",[s("p",[t._v("经过 "),s("code",[t._v("MoyaMapperPlugin")]),t._v(" 处理的后可得到转换后的 "),s("code",[t._v("Response")]),t._v(" ,如图")])])]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110220506.png",alt:""}})]),t._v(" "),s("p",[t._v("这里将请求失败进行了统一处理,无论是服务器还是自身网络的问题,"),s("code",[t._v("retStatus")]),t._v(" 都为 MMStatusCode.loadFail,但是 errorDescription 会保持原来的样子并赋值给 "),s("code",[t._v("retMsg")]),t._v("。")]),t._v(" "),s("blockquote",[s("ul",[s("li",[s("code",[t._v("retStatus")]),t._v(" 值会从枚举 "),s("code",[t._v("MMStatusCode")]),t._v(" 中取 "),s("code",[t._v("loadFail.rawValue")]),t._v(" ,即 "),s("code",[t._v("700")])]),t._v(" "),s("li",[t._v("取 类型为 "),s("code",[t._v("ModelableParameterType")]),t._v(" 的 "),s("code",[t._v("type")]),t._v(" 中 "),s("code",[t._v("statusCodeKey")]),t._v(" 所指定的值 为键名,"),s("code",[t._v("retMsg")]),t._v("也同理")])])]),t._v(" "),s("p",[t._v("ps: 这个时候可以通过判断 "),s("code",[t._v("retStatus")]),t._v(" 或 "),s("code",[t._v("response.statusCode")]),t._v(" 是否与 "),s("code",[t._v("MMStatusCode.loadFail.rawValue")]),t._v(" 相同来判断是否显示加载失败的空白页占位图")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MMStatusCode")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Int")]),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("case")]),t._v(" cache "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("230")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" loadFail "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("700")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("枚举 "),s("code",[t._v("MMStatusCode")]),t._v(" 中除了 "),s("code",[t._v("loadFail")]),t._v(" ,还有 "),s("code",[t._v("cache")]),t._v(",我们已经知道 "),s("code",[t._v("loadFail")]),t._v(" 在数据加载失败的时候会出现,那 "),s("code",[t._v("cache")]),t._v(" 是在什么时候出来呢?不急,看下一节就知道了。")]),t._v(" "),s("h2",{attrs:{id:"数据缓存"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#数据缓存"}},[t._v("#")]),t._v(" 数据缓存")]),t._v(" "),s("h5",{attrs:{id:"一、基本使用"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#一、基本使用"}},[t._v("#")]),t._v(" 一、基本使用")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("// 缓存\n@discardableResult\nMMCache.shared.cache`XXX`(value : XXX, key: String, cacheContainer: MMCache.CacheContainer = .RAM) -> Bool\n// 取舍\nMMCache.shared.fetch`XXX`Cache(key: String, cacheContainer: MMCache.CacheContainer = .RAM)\n")])])]),s("p",[t._v("缓存成功会返回一个 "),s("code",[t._v("Bool")]),t._v(" 值,这里可不接收")]),t._v(" "),s("table",[s("thead",[s("tr",[s("th",[t._v("XXX 所支持类型")]),t._v(" "),s("th")])]),t._v(" "),s("tbody",[s("tr",[s("td",[t._v("Bool")]),t._v(" "),s("td",[t._v("-")])]),t._v(" "),s("tr",[s("td",[t._v("Float")]),t._v(" "),s("td",[t._v("-")])]),t._v(" "),s("tr",[s("td",[t._v("Double")]),t._v(" "),s("td",[t._v("-")])]),t._v(" "),s("tr",[s("td",[t._v("String")]),t._v(" "),s("td",[t._v("-")])]),t._v(" "),s("tr",[s("td",[t._v("JSON")]),t._v(" "),s("td",[t._v("-")])]),t._v(" "),s("tr",[s("td",[t._v("Modelable")]),t._v(" "),s("td",[t._v("[Modelable]")])]),t._v(" "),s("tr",[s("td",[t._v("Moya.Response")]),t._v(" "),s("td",[t._v("-")])]),t._v(" "),s("tr",[s("td",[t._v("Int")]),t._v(" "),s("td",[t._v("UInt")])]),t._v(" "),s("tr",[s("td",[t._v("Int8")]),t._v(" "),s("td",[t._v("UInt8")])]),t._v(" "),s("tr",[s("td",[t._v("Int16")]),t._v(" "),s("td",[t._v("UInt16")])]),t._v(" "),s("tr",[s("td",[t._v("Int32")]),t._v(" "),s("td",[t._v("UInt32")])]),t._v(" "),s("tr",[s("td",[t._v("Int64")]),t._v(" "),s("td",[t._v("UInt64")])])])]),t._v(" "),s("blockquote",[s("p",[t._v("其中,除了 "),s("code",[t._v("Moya.Response")]),t._v(" 之外,其它类型皆是通过 "),s("code",[t._v("JSON")]),t._v(" 来实现缓存")])]),t._v(" "),s("p",[t._v("所以,如果你想清除这些类型的缓存,只需要调用如下方法即可")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token attribute atrule"}},[t._v("@discardableResult")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("removeJSONCache")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token omit keyword"}},[t._v("_")]),t._v(" key"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" cacheContainer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MMCache")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CacheContainer")]),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 constant"}},[t._v("RAM")]),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 class-name"}},[t._v("Bool")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token attribute atrule"}},[t._v("@discardableResult")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("removeAllJSONCache")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("cacheContainer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MMCache")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CacheContainer")]),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 constant"}},[t._v("RAM")]),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 class-name"}},[t._v("Bool")]),t._v("\n")])])]),s("p",[t._v("清除 "),s("code",[t._v("Moya.Response")]),t._v(" 则使用如下两个方法")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token attribute atrule"}},[t._v("@discardableResult")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("removeResponseCache")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token omit keyword"}},[t._v("_")]),t._v(" key"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),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 class-name"}},[t._v("Bool")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token attribute atrule"}},[t._v("@discardableResult")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("removeAllResponseCache")]),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 class-name"}},[t._v("Bool")]),t._v("\n")])])]),s("p",[t._v("再来看看MMCache.CacheContainer")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CacheContainer")]),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("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token constant"}},[t._v("RAM")]),t._v(" \t"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 只缓存于内存的容器")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" hybrid "),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("blockquote",[s("p",[t._v("这两种容器互不相通,即 即使key相同,使用 "),s("code",[t._v("hybrid")]),t._v(" 来缓存后,再通过 "),s("code",[t._v("RAM")]),t._v(" 取值是取不到的。")])]),t._v(" "),s("ul",[s("li",[t._v("RAM : 仅缓存于内存之中,缓存的数据在APP使用期间一直存在")]),t._v(" "),s("li",[t._v("hybrid :缓存于内存与磁盘中,APP重启后也可以获取到数据")])]),t._v(" "),s("h5",{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",[t._v("APP首次启动并进行网络请求,网络数据将缓存起来")]),t._v(" "),s("li",[t._v("APP再次启动并进行网络请求时,会先返回缓存的数据,等请求成功后再返回网络数据")]),t._v(" "),s("li",[t._v("其它情况只会加载网络数据")]),t._v(" "),s("li",[t._v("每次成功请求到数据后,都会对缓存的数据进行更新")])]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Normal")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("cacheRequest")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token omit keyword"}},[t._v("_")]),t._v(" target"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Target")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" \n cacheType"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MMCache")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CacheKeyType")]),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 keyword"}},[t._v("default")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" \n callbackQueue"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DispatchQueue")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" \n progress"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Moya")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ProgressBlock")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" \n completion"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attribute atrule"}},[t._v("@escaping")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Moya")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Completion")]),t._v("\n"),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 class-name"}},[t._v("Cancellable")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Rx")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("cacheRequest")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token omit keyword"}},[t._v("_")]),t._v(" target"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Base")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Target")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" \n callbackQueue"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DispatchQueue")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" \n cacheType"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MMCache")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CacheKeyType")]),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 keyword"}},[t._v("default")]),t._v("\n"),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 class-name"}},[t._v("Observable")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Response")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n")])])]),s("blockquote",[s("p",[t._v("实际上是对 "),s("code",[t._v("Moya")]),t._v(" 请求后的 "),s("code",[t._v("Response")]),t._v(" 进行缓存。 其实与 "),s("code",[t._v("Moya")]),t._v(" 自带的方法相比较只多了一个参数 "),s("code",[t._v("cacheType: MMCache.CacheKeyType")]),t._v(" ,定义着缓存中的 "),s("code",[t._v("key")]),t._v(" ,默认为 "),s("code",[t._v("default")])])]),t._v(" "),s("p",[t._v("下面是 "),s("code",[t._v("MMCache.CacheKeyType")]),t._v(" 的定义")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v('/**\n let cacheKey = [method]baseURL/path\n \n - default : cacheKey + "?" + parameters\n - base : cacheKey\n - custom : cacheKey + "?" + customKey\n */\npublic enum CacheKeyType {\n case `default`\n case base\n case custom(String)\n}\n')])])]),s("blockquote",[s("p",[t._v("如果你想缓存"),s("code",[t._v("多页")]),t._v("列表数据的"),s("code",[t._v("最新一页")]),t._v("数据,此时使用 "),s("code",[t._v("default")]),t._v(" 是不合适的,因为 "),s("code",[t._v("default")]),t._v(" 使用的 "),s("code",[t._v("key")]),t._v(" 包含了 "),s("code",[t._v("pageIndex")]),t._v(",这样就达不到只缓存 "),s("code",[t._v("最新一页数据")]),t._v(" 的目的, 所以这里应该使用 "),s("code",[t._v("base")]),t._v(" 或者 "),s("code",[t._v("custom(String)")])])]),t._v(" "),s("p",[t._v("我们可以来试一下带缓存的请求")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/*\n * APP第一次启动并进行网络请求,网络数据将缓存起来\n * APP再次启动并进行网络请求时,会先加载缓存,再加载网络数据\n * 其它情况只会加载网络数据\n * 每次成功请求到数据都会进行数据更新\n */")]),t._v("\nlxfNetTool"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("rx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("cacheRequest")]),s("span",{pre:!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("data")]),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 punctuation"}},[t._v(".")]),t._v("all"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" size"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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(" index"),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 function"}},[t._v("subscribe")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("onNext"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" response "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v("\n log"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("debug")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"statusCode -- ')]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[t._v("\\(")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[t._v("response"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("statusCode")]),s("span",{pre:!0,attrs:{class:"token interpolation-punctuation 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("\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("disposed")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("by"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" disposeBag"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 传统方式")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v('/*\nlet _ = lxfNetTool.cacheRequest(.data(type: .all, size: 10, index: 1)) { result in\n guard let resp = result.value else { return }\n log.debug("statusCode -- \\(resp.statusCode)")\n}\n*/')]),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("// 首次使用APP\nstatusCode -- 200\n\n// 关闭并重新打开APP,再请求一下\nstatusCode -- 230\nstatusCode -- 200\n\n// 然后再请求一下\nstatusCode -- 200\n")])])]),s("p",[t._v("这里的 "),s("code",[t._v("230")]),t._v(" 就是 "),s("code",[t._v("MMStatusCode.cache.rawValue")])]),t._v(" "),s("h2",{attrs:{id:"cocoapods"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#cocoapods"}},[t._v("#")]),t._v(" CocoaPods")]),t._v(" "),s("ul",[s("li",[t._v("默认安装")])]),t._v(" "),s("p",[t._v("MoyaMapper默认只安装Core下的文件")]),t._v(" "),s("div",{staticClass:"language-ruby extra-class"},[s("pre",{pre:!0,attrs:{class:"language-ruby"}},[s("code",[t._v("pod "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'MoyaMapper'")])]),t._v("\n")])])]),s("ul",[s("li",[t._v("RxSwift拓展")])]),t._v(" "),s("div",{staticClass:"language-ruby extra-class"},[s("pre",{pre:!0,attrs:{class:"language-ruby"}},[s("code",[t._v("pod "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'MoyaMapper/Rx'")])]),t._v("\n")])])]),s("ul",[s("li",[t._v("缓存拓展")])]),t._v(" "),s("div",{staticClass:"language-ruby extra-class"},[s("pre",{pre:!0,attrs:{class:"language-ruby"}},[s("code",[t._v("pod "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'MoyaMapper/MMCache'")])]),t._v("\n")])])]),s("ul",[s("li",[t._v("Rx缓存")])]),t._v(" "),s("div",{staticClass:"language-ruby extra-class"},[s("pre",{pre:!0,attrs:{class:"language-ruby"}},[s("code",[t._v("pod "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'MoyaMapper/RxCache'")])]),t._v("\n")])])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/108.3c1de7a6.js b/assets/js/108.3c1de7a6.js new file mode 100644 index 000000000..ed7dcc868 --- /dev/null +++ b/assets/js/108.3c1de7a6.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[108],{419:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("p",[t._v("相信使用 "),s("code",[t._v("ReactorKit")]),t._v(" + "),s("code",[t._v("RxDataSources")]),t._v(" 的同学都有遇到列表会多次刷新的问题吧,本篇将提出我的解决方案,相互学习交流")]),t._v(" "),s("h2",{attrs:{id:"一、常规使用"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#一、常规使用"}},[t._v("#")]),t._v(" 一、常规使用")]),t._v(" "),s("p",[s("code",[t._v("Reactor")])]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Mutation")]),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("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("setSections")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFSection")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token operator"}},[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("struct")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("State")]),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(" sections "),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 class-name"}},[t._v("LXFSection")]),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 punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[s("code",[t._v("View")])]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("reactor.state.map "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$0")]),t._v(".sections "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n .bind"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("to: tableView.rx.items"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("dataSource: dataSource"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("))")]),t._v("\n .disposed"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("by: disposeBag"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("由于在 "),s("code",[t._v("ReactorKit")]),t._v(" 中,"),s("code",[t._v("View")]),t._v(" 对状态的订阅都是针对 "),s("code",[t._v("State")]),t._v(" 来说的,而非 "),s("code",[t._v("State")]),t._v(" 中的属性,所以只要 "),s("code",[t._v("State")]),t._v(" 的值发生改变,"),s("code",[t._v("View")]),t._v(" 中所有对 "),s("code",[t._v("State")]),t._v(" 的订阅回调都会被调用,从而使视图进行更新。")]),t._v(" "),s("p",[t._v("但是一个属性的变化,理应只需要更新对应的视图即可,要达到这一效果,就需要使用 "),s("code",[t._v("distinctUntilChanged")]),t._v(" 这个方法,对于 "),s("code",[t._v("State")]),t._v(" 中遵守了 "),s("code",[t._v("Equatable")]),t._v(" 协议的属性,直接加上 "),s("code",[t._v(".distinctUntilChanged()")]),t._v(" 即可")]),t._v(" "),s("p",[t._v("但是看我们定义的 "),s("code",[t._v("Section")])]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("RxDataSources")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFSection")]),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("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("list")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFSectionItem")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("extension")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFSection")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SectionModelType")]),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("init")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("original"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFSection")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" items"),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 class-name"}},[t._v("LXFSectionItem")]),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("switch")]),t._v(" original "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("list"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),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 function"}},[t._v("list")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("items"),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 keyword"}},[t._v("var")]),t._v(" items"),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 class-name"}},[t._v("LXFSectionItem")]),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("switch")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),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("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("list")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" items"),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("return")]),t._v(" items\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\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFSectionItem")]),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("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("item")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFCellReactor")]),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("LXFSection")]),t._v(" 遵守 "),s("code",[t._v("Equatable")]),t._v(" 并实现协议方法,或者是在 "),s("code",[t._v("distinctUntilChanged")]),t._v(" 回调中自己做判断 ,但都太麻烦了~")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[t._v("reactor"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("state"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("map "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token short-argument"}},[t._v("$0")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sections "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("distinctUntilChanged")]),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("sectionArr1"),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 class-name"}},[t._v("LXFSection")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" sectionArr2"),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 class-name"}},[t._v("LXFSection")]),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("in")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" 判断 sectionArr1 和 sectionArr2 是否相等\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 function"}},[t._v("bind")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("to"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" tableView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("rx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("items")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("dataSource"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" dataSource"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 function"}},[t._v("disposed")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("by"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" disposeBag"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("那有没有其它办法呢?答案当然是有的")]),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("SwiftyTraceableValue")]),t._v(" 的结构体,定义如下:")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("public")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SwiftyTraceableValue")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("T")]),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("public")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" tracker"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Int")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),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("var")]),t._v(" value"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("T")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("内部属性说明:")]),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("tracker")]),t._v(" "),s("td",[t._v("用于判断是否数据是否有发生变化")])]),t._v(" "),s("tr",[s("td",[t._v("value")]),t._v(" "),s("td",[t._v("存储原来的值")])])])]),t._v(" "),s("p",[t._v("调整 "),s("code",[t._v("Mutation")])]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Mutation")]),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("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("setSections")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TraceableValue")]),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 class-name"}},[t._v("LXFSection")]),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 punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token operator"}},[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("State")])]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("State")]),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(" sections "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TraceableValue")]),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 class-name"}},[t._v("LXFSection")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),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 keyword"}},[t._v("init")]),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 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 operator"}},[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("Sections")]),t._v(" 数据的处理")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" oldTracker "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("currentState"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sections"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("tracker "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("??")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" oldSections "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("currentState"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sections"),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 punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\nitems "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" oldSections"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("first"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("items "),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(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" items\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" finalSections "),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("LXFSection")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("list")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("items"),s("span",{pre:!0,attrs:{class:"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("let")]),t._v(" sections "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TraceableValue")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("tracker"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" oldTracker "),s("span",{pre:!0,attrs:{class:"token operator"}},[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(" value"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" finalSections"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" setSections "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Observable")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("just")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Mutation")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("setSections")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("sections"),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("View")])]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[t._v("reactor"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("state"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("map "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token short-argument"}},[t._v("$0")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sections "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("distinctUntilChanged "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token short-argument"}},[t._v("$0")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("tracker "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token short-argument"}},[t._v("$1")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("tracker\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("map "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token short-argument"}},[t._v("$0")]),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 "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("bind")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("to"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" tableView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("rx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("items")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("dataSource"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" dataSource"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 function"}},[t._v("disposed")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("by"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" disposeBag"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("至此,按如上的内容进行调整便可以解决列表多次刷新的问题,但是这改动也忒大了,而且还要自己管理 "),s("code",[t._v("tracker")]),t._v(" 的值,这怎么能忍?")]),t._v(" "),s("p",[t._v("那有什么办法可以让我们不再手动管理 "),s("code",[t._v("tracker")]),t._v(" 呢?🤔")]),t._v(" "),s("p",[t._v("这里我们可以使用 "),s("code",[t._v("Swift")]),t._v(" 的 "),s("code",[t._v("Property Wrapper")]),t._v("😏")]),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("SwiftyTraceableValue")]),t._v(" 使用 "),s("code",[t._v("Property Wrapper")]),t._v(" 进行封装")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token attribute atrule"}},[t._v("@propertyWrapper")]),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("struct")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SwiftyTraceableValue")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("T")]),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("public")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" tracker"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Int")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),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("var")]),t._v(" value"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("T")]),t._v("\n \n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("public")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" projectedValue"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SwiftyTraceableValue")]),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("self")]),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("public")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" wrappedValue"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("T")]),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("get")]),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("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("value\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("set")]),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("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("tracker "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),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(" newValue\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 keyword"}},[t._v("public")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("init")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("wrappedValue"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("T")]),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("self")]),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(" wrappedValue\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("tracker")]),t._v(" 加 "),s("code",[t._v("1")]),t._v("。这里 "),s("code",[t._v("projectedValue")]),t._v(" 我们返回真实类型 "),s("code",[t._v("SwiftyTraceableValue")]),t._v(" ,以便后续使用。")]),t._v(" "),s("p",[t._v("这里简单提一下属性包装器的使用,更加具体详细的内容可以看我另一篇文章《"),s("RouterLink",{attrs:{to:"/pages/b0e486/"}},[t._v("Swift - PropertyWrapper")]),t._v("》")],1),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 使用 @SwiftyTraceableValue 对属性进行修饰")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token attribute atrule"}},[t._v("@SwiftyTraceableValue")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" sections "),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 class-name"}},[t._v("LXFSection")]),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 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("// 获取 wrappedValue 的值")]),t._v("\nsections\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 获取 projectedValue 的值")]),t._v("\n$sections\n")])])]),s("p",[t._v("不仅如此,我们还为 "),s("code",[t._v("SwiftyTraceableValue")]),t._v(" 专门扩展 "),s("code",[t._v("ObservableType")]),t._v(",将进行比较内容部分封装起来")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("public")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extension")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ObservableType")]),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("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("mapDistinctUntilTraceableValueChanged")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("T")]),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 omit keyword"}},[t._v("_")]),t._v(" transform"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attribute atrule"}},[t._v("@escaping")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Element")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("throws")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("->")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SwiftyTraceableValue")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("T")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n "),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 class-name"}},[t._v("Observable")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("T")]),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("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),t._v("\n "),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("(")]),t._v("transform"),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("distinctUntilChanged "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token short-argument"}},[t._v("$0")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("tracker "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token short-argument"}},[t._v("$1")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("tracker "),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("map "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token short-argument"}},[t._v("$0")]),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 "),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("h2",{attrs:{id:"四、使用"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#四、使用"}},[t._v("#")]),t._v(" 四、使用")]),t._v(" "),s("blockquote",[s("p",[t._v("以下内容是基于第一部分进行调整的,大家可以忘掉第二部分的修改了")])]),t._v(" "),s("p",[t._v("最终我们只需要调整两处地方即可解决列表多次刷新的问题!")]),t._v(" "),s("p",[s("code",[t._v("Reactor")])]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("State")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token attribute atrule"}},[t._v("@SwiftyTraceableValue")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" sections "),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 class-name"}},[t._v("LXFSection")]),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 punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[s("code",[t._v("View")])]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[t._v("reactor"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("state"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("mapDistinctUntilTraceableValueChanged "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token short-argument"}},[t._v("$0")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("$sections "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("bind")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("to"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" tableView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("rx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("items")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("dataSource"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" dataSource"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 function"}},[t._v("disposed")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("by"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" disposeBag"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("现在,我将它做成了开源库,方便大家使用,好用的话请 "),s("code",[t._v("Star")]),t._v(" 吧 😃")]),t._v(" "),s("p",[t._v("GitHub:\n"),s("a",{attrs:{href:"https://github.com/LinXunFeng/SwiftyTraceableValue",target:"_blank",rel:"noopener noreferrer"}},[t._v("SwiftyTraceableValue"),s("OutboundLink")],1)])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/109.4531e506.js b/assets/js/109.4531e506.js new file mode 100644 index 000000000..5c5fbae09 --- /dev/null +++ b/assets/js/109.4531e506.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[109],{422:function(s,t,a){"use strict";a.r(t);var e=a(8),r=Object(e.a)({},(function(){var s=this,t=s._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[t("h2",{attrs:{id:"安装openssh"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#安装openssh"}},[s._v("#")]),s._v(" 安装OpenSSH")]),s._v(" "),t("p",[s._v("本文内容的前提是设备已越狱!")]),s._v(" "),t("blockquote",[t("p",[t("code",[s._v("Secure Shell(SSH)")]),s._v(" 是建立在应用层基础上的安全协议,用于计算机之间的加密登录,可以在不安全的网络中为网络服务提供安全的传输环境")])]),s._v(" "),t("p",[s._v("在 "),t("code",[s._v("Cydia")]),s._v(" 搜索 "),t("code",[s._v("OpenSSH")]),s._v(" ,找到后安装即可")]),s._v(" "),t("p",[t("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112181715108.jpg",alt:"OpenSSH"}})]),s._v(" "),t("h2",{attrs:{id:"账号"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#账号"}},[s._v("#")]),s._v(" 账号")]),s._v(" "),t("blockquote",[t("p",[t("code",[s._v("root")]),s._v(":最高权限账号,"),t("code",[s._v("$HOME")]),s._v(" 为 "),t("code",[s._v("/var/root")])])]),s._v(" "),t("ul",[t("li",[s._v("账号:"),t("code",[s._v("root")])]),s._v(" "),t("li",[s._v("密码:"),t("code",[s._v("alpine")])])]),s._v(" "),t("p",[s._v("修改登录密码")]),s._v(" "),t("ol",[t("li",[s._v("登录 "),t("code",[s._v("root")]),s._v(" 账号")]),s._v(" "),t("li",[s._v("修改 "),t("code",[s._v("root")]),s._v(" 账号的密码,终端输入:"),t("code",[s._v("passwd")])])]),s._v(" "),t("h2",{attrs:{id:"远程登录"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#远程登录"}},[s._v("#")]),s._v(" 远程登录")]),s._v(" "),t("h3",{attrs:{id:"方式一-账号密码"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#方式一-账号密码"}},[s._v("#")]),s._v(" 方式一:账号密码")]),s._v(" "),t("p",[s._v('前往iOS设备的 "设置" -> "Wi-Fi" 页面,找到当前已经连接的网络,查看其 '),t("code",[s._v("IP")]),s._v(" 地址")]),s._v(" "),t("p",[t("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112181717429.png",alt:"IP地址"}})]),s._v(" "),t("p",[s._v("如上图显示的 "),t("code",[s._v("IP")]),s._v(" 地址为 "),t("code",[s._v("192.168.10.104")]),s._v(",则在终端内输入:")]),s._v(" "),t("div",{staticClass:"language-shell extra-class"},[t("pre",{pre:!0,attrs:{class:"language-shell"}},[t("code",[t("span",{pre:!0,attrs:{class:"token function"}},[s._v("ssh")]),s._v(" root@192.168.10.104\n")])])]),t("p",[s._v("输入密码:"),t("code",[s._v("alpine")])]),s._v(" "),t("p",[t("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112181718774.png",alt:"ssh"}})]),s._v(" "),t("h3",{attrs:{id:"方式二-免密码登录"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#方式二-免密码登录"}},[s._v("#")]),s._v(" 方式二:免密码登录")]),s._v(" "),t("p",[s._v("相较于方式一,不用每次连接 "),t("code",[s._v("SSH")]),s._v(" 时都提示输入密码")]),s._v(" "),t("table",[t("thead",[t("tr",[t("th",[s._v("角色")]),s._v(" "),t("th",[s._v("设备")]),s._v(" "),t("th",[s._v("文件")])])]),s._v(" "),t("tbody",[t("tr",[t("td",[s._v("客户端")]),s._v(" "),t("td",[t("code",[s._v("Mac")])]),s._v(" "),t("td",[s._v("公钥 "),t("code",[s._v("~/.ssh/id_rsa.pub")]),t("br"),s._v("私钥 "),t("code",[s._v("~/.ssh/id_rsa")])])]),s._v(" "),t("tr",[t("td",[s._v("服务器端")]),s._v(" "),t("td",[t("code",[s._v("iPhone")])]),s._v(" "),t("td",[s._v("授权文件 "),t("code",[s._v("~/.ssh/authorized_keys")])])])])]),s._v(" "),t("h4",{attrs:{id:"_1、生成秘钥对"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1、生成秘钥对"}},[s._v("#")]),s._v(" 1、生成秘钥对")]),s._v(" "),t("div",{staticClass:"language-shell extra-class"},[t("pre",{pre:!0,attrs:{class:"language-shell"}},[t("code",[s._v("ssh-keygen "),t("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("-t")]),s._v(" rsa\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# OpenSSH默认生成的是RSA密钥,-t 参数是指定密钥类型")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 所以上面的命令可以省略 -t 参数:ssh-keygen")]),s._v("\n")])])]),t("h4",{attrs:{id:"_2、将公钥上传至-ios-设备"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2、将公钥上传至-ios-设备"}},[s._v("#")]),s._v(" 2、将公钥上传至 "),t("code",[s._v("iOS")]),s._v(" 设备")]),s._v(" "),t("p",[s._v("方式一:自动(推荐)")]),s._v(" "),t("p",[s._v("执行如下命令,输入密码即可")]),s._v(" "),t("div",{staticClass:"language-shell extra-class"},[t("pre",{pre:!0,attrs:{class:"language-shell"}},[t("code",[s._v("ssh-copy-id root@192.168.10.104\n")])])]),t("p",[s._v("方式二:手动")]),s._v(" "),t("div",{staticClass:"language-shell extra-class"},[t("pre",{pre:!0,attrs:{class:"language-shell"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 该方式会直接覆盖 authorized_keys 文件")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("scp")]),s._v(" ~/.ssh/id_rsa.pub root@192.168.10.104:/var/root/.ssh/authorized_keys\n")])])]),t("p",[t("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112181719876.png",alt:"scp id_rsa.pub"}})]),s._v(" "),t("p",[s._v("鉴于会将 "),t("code",[s._v("authorized_keys")]),s._v(" 文件覆盖,所以最好还是先将 "),t("code",[s._v("id_rsa.pub")]),s._v(" 上传到设备后再追加到 "),t("code",[s._v("authorized_keys")])]),s._v(" "),t("div",{staticClass:"language-shell extra-class"},[t("pre",{pre:!0,attrs:{class:"language-shell"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 上传公钥至 root 的家目录")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("scp")]),s._v(" ~/.ssh/id_rsa.pub root@192.168.10.104:~\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 登录 iOS 设备")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("ssh")]),s._v(" root@192.168.10.104\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 向 authorized_keys 追加内容")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("cat")]),s._v(" ~/id_rsa.pub "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">>")]),s._v(" ~/.ssh/authorized_keys\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 移除上传的公钥")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("rm")]),s._v(" ~/id_rsa.pub\n")])])]),t("p",[t("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112181719941.png",alt:"authorized_keys追加内容"}})]),s._v(" "),t("p",[s._v("现在再次进行登录,便无须输入密码即可直接登录成功了")]),s._v(" "),t("h4",{attrs:{id:"_3、注意事项"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3、注意事项"}},[s._v("#")]),s._v(" 3、注意事项")]),s._v(" "),t("p",[s._v("如果 "),t("code",[s._v("iOS")]),s._v(" 设备上不存在 "),t("code",[s._v("/var/root/.ssh")]),s._v(" 目录,会报如下错误")]),s._v(" "),t("div",{staticClass:"language-shell extra-class"},[t("pre",{pre:!0,attrs:{class:"language-shell"}},[t("code",[s._v("scp: /var/root/.ssh/authorized_keys: No such "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("file")]),s._v(" or directory\n")])])]),t("p",[s._v("需要先登录设备创建该目录")]),s._v(" "),t("div",{staticClass:"language-shell extra-class"},[t("pre",{pre:!0,attrs:{class:"language-shell"}},[t("code",[s._v("xunfengteki-iPad:~ root"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# cd /var/root/")]),s._v("\nxunfengteki-iPad:~ root"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# mkdir .ssh")]),s._v("\n")])])]),t("p",[s._v("如果在经过配置后还是要求输入密码,那可能是文件的权限不足,需要登录到 "),t("code",[s._v("iOS")]),s._v(" 设备后,按如下命令对这些文件或目录进行权限调整即可")]),s._v(" "),t("div",{staticClass:"language-shell extra-class"},[t("pre",{pre:!0,attrs:{class:"language-shell"}},[t("code",[t("span",{pre:!0,attrs:{class:"token function"}},[s._v("chmod")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("755")]),s._v(" ~\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 设置 .ssh 目录权限")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("chmod")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("755")]),s._v(" ~/.ssh\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 设置 authorized_keys 权限")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("chmod")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("644")]),s._v(" ~/.ssh/authorized_keys\n")])])]),t("h2",{attrs:{id:"usb连接设备"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#usb连接设备"}},[s._v("#")]),s._v(" USB连接设备")]),s._v(" "),t("blockquote",[t("p",[s._v("前面所提及的都是通过 "),t("code",[s._v("Wi-Fi")]),s._v(" 方式连接设备,这种方式会有个很大的弊端,那就是当网络不通畅时会卡顿,为了保证传输速度,可以通过 "),t("code",[s._v("USB")]),s._v(" 连接的方式解决这个问题")])]),s._v(" "),t("h3",{attrs:{id:"_1、安装-usbmuxd"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1、安装-usbmuxd"}},[s._v("#")]),s._v(" 1、安装 "),t("code",[s._v("usbmuxd")])]),s._v(" "),t("div",{staticClass:"language-shell extra-class"},[t("pre",{pre:!0,attrs:{class:"language-shell"}},[t("code",[s._v("brew "),t("span",{pre:!0,attrs:{class:"token function"}},[s._v("install")]),s._v(" usbmuxd\n")])])]),t("h3",{attrs:{id:"_2、端口映射"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2、端口映射"}},[s._v("#")]),s._v(" 2、端口映射")]),s._v(" "),t("p",[s._v("本地端口 "),t("code",[s._v("2222")]),s._v(" 映射 远程端口 "),t("code",[s._v("22")])]),s._v(" "),t("div",{staticClass:"language-shell extra-class"},[t("pre",{pre:!0,attrs:{class:"language-shell"}},[t("code",[s._v("iproxy "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2222")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("22")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("设备UDID"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n")])])]),t("p",[s._v("如果有多台 "),t("code",[s._v("iOS")]),s._v(" 设备连接到同一台 "),t("code",[s._v("Mac")]),s._v(" 上,则需要在命令后面加上设备的 "),t("code",[s._v("UDID")]),s._v(",如果只有一台时,则可省略")]),s._v(" "),t("h3",{attrs:{id:"_3、连接设备"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3、连接设备"}},[s._v("#")]),s._v(" 3、连接设备")]),s._v(" "),t("div",{staticClass:"language-shell extra-class"},[t("pre",{pre:!0,attrs:{class:"language-shell"}},[t("code",[t("span",{pre:!0,attrs:{class:"token function"}},[s._v("ssh")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("-p")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[s._v("2222")]),s._v(" root@127.0.0.1\n")])])]),t("p",[t("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112181720044.png",alt:"USB连接"}})]),s._v(" "),t("h2",{attrs:{id:"封装常用命令"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#封装常用命令"}},[s._v("#")]),s._v(" 封装常用命令")]),s._v(" "),t("blockquote",[t("p",[s._v("Shuttle: 简易的终端命令快捷菜单,官网:"),t("a",{attrs:{href:"http://fitztrev.github.io/shuttle/",target:"_blank",rel:"noopener noreferrer"}},[s._v("http://fitztrev.github.io/shuttle/"),t("OutboundLink")],1)])]),s._v(" "),t("div",{staticClass:"language-shell extra-class"},[t("pre",{pre:!0,attrs:{class:"language-shell"}},[t("code",[t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"iOS"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"USB连接iPhone"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"cmd"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"iproxy 2222 22"')]),s._v(",\n "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"inTerminal"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"new"')]),s._v(",\n "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"name"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"1.USB映射SSH端口"')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v(",\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("{")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"cmd"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ssh root@localhost -p 2222"')]),s._v(",\n "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"inTerminal"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"new"')]),s._v(",\n "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"name"')]),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v(":")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"2.连接iPhone"')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("}")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n")])])]),t("p",[s._v("效果:")]),s._v(" "),t("p",[t("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112181721122.jpg",alt:"shuttle"}})]),s._v(" "),t("p",[s._v("现在只需要按顺序点击 "),t("code",[s._v("iOS")]),s._v(" -> "),t("code",[s._v("USB连接iPhone")]),s._v(" 下的 "),t("code",[s._v("1.USB映射SSH端口")]),s._v(" 和 "),t("code",[s._v("2.连接iPhone")]),s._v(",即可快速使用 "),t("code",[s._v("usb")]),s._v(" 连接我们的 "),t("code",[s._v("iOS")]),s._v(" 设备,提升效率。")]),s._v(" "),t("h2",{attrs:{id:"中文问题"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#中文问题"}},[s._v("#")]),s._v(" 中文问题")]),s._v(" "),t("p",[t("code",[s._v("iOS")]),s._v(" 的终端在默认情况下是不支持中文的输入和显示,当你输入中文时会自动清空当前输入的所有内容。")]),s._v(" "),t("p",[s._v("解决方案:在家目录下新建 "),t("code",[s._v(".inputrc")]),s._v(" 文件")]),s._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("touch ~/.inputrc\n")])])]),t("p",[s._v("文件内容:")]),s._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[s._v("# 不将中文字符转化为转文序列\nset convert-meta off\n\n# 允许向终端输出中文\nset output-meta on\n\n# 允许向终端输入中文\nset meta-flag on\nset input-meta on\n")])])]),t("p",[s._v("如果想要 "),t("code",[s._v("iOS")]),s._v(" 设备的终端上编辑文件内容,需要在 "),t("code",[s._v("Cydia")]),s._v(" 搜索 "),t("code",[s._v("Vi IMproved")]),s._v(" 并安装")]),s._v(" "),t("p",[t("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112181724254.jpg",alt:""}})]),s._v(" "),t("p",[s._v("安装完成后,在终端输入:")]),s._v(" "),t("div",{staticClass:"language-shell extra-class"},[t("pre",{pre:!0,attrs:{class:"language-shell"}},[t("code",[t("span",{pre:!0,attrs:{class:"token function"}},[s._v("vi")]),s._v(" ~/.inputrc\n")])])]),t("p",[s._v("按 "),t("code",[s._v("i")]),s._v(" 切换到输入模式,将上述文件内容粘贴进去,按 "),t("code",[s._v("Esc")]),s._v(" 切换到末行模式,输入 "),t("code",[s._v(":wq")]),s._v(" 回车保存退出。")]),s._v(" "),t("p",[s._v("现在中文是可以输入了,但是显示还是有问题的,比如我创建了一个 "),t("code",[s._v("测试.txt")]),s._v(",显示的是 "),t("code",[s._v("\\346\\265\\213\\350\\257\\225.txt")])]),s._v(" "),t("p",[t("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112181725878.png",alt:""}})]),s._v(" "),t("p",[s._v("解决方案:通过 "),t("code",[s._v("Cydia")]),s._v(" 搜索 "),t("code",[s._v("Locale Profiles in UTF-8")]),s._v(" 并安装即可(软件源 "),t("code",[s._v("BigBoss")]),s._v(":"),t("code",[s._v("http://apt.thebigboss.org/repofiles/cydia/")]),s._v(")")]),s._v(" "),t("p",[t("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112181725723.jpg",alt:""}})]),s._v(" "),t("p",[s._v("这时退出 "),t("code",[s._v("iOS")]),s._v(" 终端,还重新进入即可解决!")]),s._v(" "),t("p",[s._v("点击详情页底部的 "),t("code",[s._v("文件系统内容")])]),s._v(" "),t("p",[t("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112181726035.jpg",alt:""}})]),s._v(" "),t("p",[s._v("可以看到该插件帮我们在 "),t("code",[s._v("/usr/share/locale")]),s._v(" 目录下安装了中文语言包,并在 "),t("code",[s._v("/etc/profile.d/")]),s._v(" 目录下创建了 "),t("code",[s._v("localeutf8.sh")]),s._v(" 文件,设置当前语言为 "),t("code",[s._v("en_US.UTF-8")])]),s._v(" "),t("p",[t("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112181727214.png",alt:""}})]),s._v(" "),t("div",{staticClass:"language-shell extra-class"},[t("pre",{pre:!0,attrs:{class:"language-shell"}},[t("code",[s._v("xunfengtekiiPhone:~ root"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# cat /etc/profile.d/localeutf8.sh")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("export")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t("span",{pre:!0,attrs:{class:"token environment constant"}},[s._v("LANG")])]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"en_US.UTF-8"')]),s._v("\n")])])])])}),[],!1,null,null,null);t.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/11.2331f9b8.js b/assets/js/11.2331f9b8.js new file mode 100644 index 000000000..4a8b38eb7 --- /dev/null +++ b/assets/js/11.2331f9b8.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[11],{325:function(t,n,s){"use strict";s.r(n);var e=s(8),o=Object(e.a)({},(function(){return(0,this._self._c)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);n.default=o.exports}}]); \ No newline at end of file diff --git a/assets/js/110.1238c012.js b/assets/js/110.1238c012.js new file mode 100644 index 000000000..b0c824c82 --- /dev/null +++ b/assets/js/110.1238c012.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[110],{421:function(t,a,e){"use strict";e.r(a);var s=e(8),_=Object(s.a)({},(function(){var t=this,a=t._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("p",[a("code",[t._v("Mach-O")]),t._v(" 是 "),a("code",[t._v("iOS/macOS")]),t._v(" 系统上应用程序的文件格式,了解 "),a("code",[t._v("Mach-O")]),t._v(" 文件的格式,有利于我们后续对应用进行静态分析和动态调试。")]),t._v(" "),a("h2",{attrs:{id:"分析-mach-o-文件的工具"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#分析-mach-o-文件的工具"}},[t._v("#")]),t._v(" 分析 "),a("code",[t._v("Mach-O")]),t._v(" 文件的工具")]),t._v(" "),a("h3",{attrs:{id:"otool"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#otool"}},[t._v("#")]),t._v(" "),a("code",[t._v("otool")])]),t._v(" "),a("p",[t._v("此为命令行的方式,具体参数可以使用 "),a("code",[t._v("man")]),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 function"}},[t._v("man")]),t._v(" otool\n")])])]),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".\n"),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-h")]),t._v(" Display the Mach header.\n"),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-l")]),t._v(" Display the load commands.\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".\n")])])]),a("p",[t._v("其中 "),a("code",[t._v("-h")]),t._v(" 可查看 "),a("code",[t._v("Header")])]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("otool "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-h")]),t._v(" Mach-O文件\n")])])]),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112261628505.png",alt:"otool -h"}})]),t._v(" "),a("p",[a("code",[t._v("-l")]),t._v(" 可查看 "),a("code",[t._v("load commands")]),t._v(",打印的内容太多就不展示了,有兴趣的可以自己打印看看")]),t._v(" "),a("h3",{attrs:{id:"machoview"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#machoview"}},[t._v("#")]),t._v(" "),a("code",[t._v("MachOView")])]),t._v(" "),a("p",[t._v("免费开源的 "),a("code",[t._v("Mach-O")]),t._v(" 文件分析工具")]),t._v(" "),a("ul",[a("li",[t._v("GitHub链接:"),a("a",{attrs:{href:"https://github.com/gdbinit/MachOView",target:"_blank",rel:"noopener noreferrer"}},[t._v("gdbinit/MachOView"),a("OutboundLink")],1)]),t._v(" "),a("li",[t._v("蓝奏云链接:"),a("a",{attrs:{href:"https://linxunfeng.lanzoul.com/iinjCxuj8te",target:"_blank",rel:"noopener noreferrer"}},[t._v("MachOView.dmg"),a("OutboundLink")],1)])]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112261628293.png",alt:"MachOView"}})]),t._v(" "),a("h3",{attrs:{id:"_010-editor"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_010-editor"}},[t._v("#")]),t._v(" "),a("code",[t._v("010 Editor")])]),t._v(" "),a("ul",[a("li",[t._v("官网链接: "),a("a",{attrs:{href:"https://www.sweetscape.com/download/010editor/",target:"_blank",rel:"noopener noreferrer"}},[t._v("010 Editor"),a("OutboundLink")],1)]),t._v(" "),a("li",[t._v("MachO模板:"),a("a",{attrs:{href:"https://github.com/strazzere/010Editor-stuff/blob/master/Templates/MachOTemplate.bt",target:"_blank",rel:"noopener noreferrer"}},[t._v("MachOTemplate.bt"),a("OutboundLink")],1)])]),t._v(" "),a("p",[a("code",[t._v("010 Editor")]),t._v(" 的模板功能很强大,收费产品,不过要分析 "),a("code",[t._v("ARM64")]),t._v(" 架构的 "),a("code",[t._v("Mach-O")]),t._v(" 程序,需要借助第三方模板。")]),t._v(" "),a("p",[t._v("菜单依次选择:"),a("code",[t._v("Templates")]),t._v(" -> "),a("code",[t._v("View Installed Templates")])]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112261629510.png",alt:"Add Template"}})]),t._v(" "),a("p",[t._v("点击 "),a("code",[t._v("Add")]),t._v(" 按钮,选择下载好的 "),a("code",[t._v("MachOTemplate.bt")]),t._v(",可以配置 "),a("code",[t._v("Name")]),t._v(" 和 "),a("code",[t._v("Category")]),t._v(" 等,然后点击 "),a("code",[t._v("OK")])]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112261630329.png",alt:""}})]),t._v(" "),a("p",[t._v("回到程序,将 "),a("code",[t._v("Mach-O")]),t._v(" 插入到 "),a("code",[t._v("010 Editor")]),t._v("中,接着在 "),a("code",[t._v("Templates")]),t._v(" 菜单中选择刚才点击的模板")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112261630148.png",alt:""}})]),t._v(" "),a("p",[t._v("分析结果如图所示\n"),a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112261632164.png",alt:""}})]),t._v(" "),a("h2",{attrs:{id:"mach-o-的结构"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#mach-o-的结构"}},[t._v("#")]),t._v(" "),a("code",[t._v("Mach-O")]),t._v(" 的结构")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112261632280.png",alt:"mach-o-structure-official"}})]),t._v(" "),a("p",[t._v("如上图所示,"),a("code",[t._v("Mach-O")]),t._v(" 文件由三部分组成")]),t._v(" "),a("table",[a("thead",[a("tr",[a("th",[t._v("部分")]),t._v(" "),a("th",[t._v("作用")])])]),t._v(" "),a("tbody",[a("tr",[a("td",[t._v("Mach-O头部("),a("code",[t._v("Header")]),t._v(")")]),t._v(" "),a("td",[t._v("保存了 "),a("code",[t._v("CPU")]),t._v(" 架构、大小端序、文件类型、加载命令数量等一些基本信息")])]),t._v(" "),a("tr",[a("td",[t._v("加载命令("),a("code",[t._v("Load Commands")]),t._v(")")]),t._v(" "),a("td",[t._v("指定了文件的逻辑结构与文件在虚拟内存中的布局")])]),t._v(" "),a("tr",[a("td",[t._v("数据块("),a("code",[t._v("Data")]),t._v(")")]),t._v(" "),a("td",[a("code",[t._v("Load Commands")]),t._v(" 中定义的 "),a("code",[t._v("Segment")]),t._v(" 的原始数据")])])])]),t._v(" "),a("h3",{attrs:{id:"header"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#header"}},[t._v("#")]),t._v(" "),a("code",[t._v("Header")])]),t._v(" "),a("blockquote",[a("p",[a("code",[t._v("Mach-o")]),t._v(" 头部("),a("code",[t._v("Header")]),t._v(")保存了 "),a("code",[t._v("CPU")]),t._v(" 架构、大小端序、文件类型、加载命令数量等一些基本信息,用于校验 "),a("code",[t._v("Mach-O")]),t._v(" 文件的合法性和确定文件的运行环境。")])]),t._v(" "),a("p",[t._v("在 "),a("code",[t._v("Xcode")]),t._v(" 中按快捷键 "),a("code",[t._v("⌘ + Shift + o")]),t._v(" ,输入 "),a("code",[t._v("mach-o/loader.h")]),t._v(",即可找到头部的定义")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112261635231.png",alt:"mach-o/loader.h"}})]),t._v(" "),a("p",[a("code",[t._v("32")]),t._v(" 位和 "),a("code",[t._v("64")]),t._v(" 位架构的 "),a("code",[t._v("CPU")]),t._v(" 分别使用 "),a("code",[t._v("mach_header")]),t._v(" 与 "),a("code",[t._v("mach_header_64")]),t._v(" 结构体来描述 "),a("code",[t._v("Mach-O")]),t._v(" 头部,本文所述内容均以 "),a("code",[t._v("64")]),t._v(" 位为主,定义如下:")]),t._v(" "),a("div",{staticClass:"language-c extra-class"},[a("pre",{pre:!0,attrs:{class:"language-c"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/*\n * The 64-bit mach header appears at the very beginning of object files for\n * 64-bit architectures.\n */")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("mach_header_64")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v(" magic"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* mach magic number identifier */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("cpu_type_t")]),t._v(" cputype"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* cpu specifier */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("cpu_subtype_t")]),t._v(" cpusubtype"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* machine specifier */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v(" filetype"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* type of file */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v(" ncmds"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* number of load commands */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v(" sizeofcmds"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* the size of all the load commands */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v(" flags"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* flags */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v(" reserved"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* reserved */")]),t._v("\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 comment"}},[t._v("/* Constant for the magic field of the mach_header_64 (64-bit architectures) */")]),t._v("\n"),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("define")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token macro-name"}},[t._v("MH_MAGIC_64")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token expression"}},[a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0xfeedfacf")]),t._v(" ")]),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* the 64-bit mach magic number */")])]),t._v("\n"),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("define")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token macro-name"}},[t._v("MH_CIGAM_64")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token expression"}},[a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0xcffaedfe")]),t._v(" ")]),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* NXSwapInt(MH_MAGIC_64) */")])]),t._v("\n")])])]),a("table",[a("thead",[a("tr",[a("th",[t._v("字段")]),t._v(" "),a("th",[t._v("作用")])])]),t._v(" "),a("tbody",[a("tr",[a("td",[a("code",[t._v("magic")])]),t._v(" "),a("td",[t._v("魔数(特征字段),用于标识当前设备是大端序还是小端序。"),a("br"),t._v("由于 "),a("code",[t._v("iOS")]),t._v(" 是小端序,所以其被定义常量 "),a("code",[t._v("MH_MAGIC_64")]),t._v(",即固定取值为 "),a("code",[t._v("0xfeedfacf")])])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("cputype")])]),t._v(" "),a("td",[t._v("标识 "),a("code",[t._v("CPU")]),t._v(" 架构,类型为 "),a("code",[t._v("cpu_type_t")]),t._v(",其定义于 "),a("code",[t._v("mach/machine.h")])])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("cpusubtype")])]),t._v(" "),a("td",[t._v("标具体的 "),a("code",[t._v("CPU")]),t._v(" 架构,区分不同版本的处理器,类型为 "),a("code",[t._v("cpusubtype")]),t._v(",其定义于 "),a("code",[t._v("mach/machine.h")])])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("filetype")])]),t._v(" "),a("td",[a("code",[t._v("Mach-O")]),t._v(" 文件类型(如:可执行文件、库文件等),可在 "),a("code",[t._v("mach-o/loader.h")]),t._v(" 中找到具体定义和取值。"),a("br"),t._v("常见的有 "),a("code",[t._v("MH_OBJECT")]),t._v("(中间目标文件)、"),a("code",[t._v("MH_EXECUTE")]),t._v("(可执行文件)、"),a("code",[t._v("MH_DYLIB")]),t._v("(动态链接库)、"),a("code",[t._v("MH_DYLINKER")]),t._v("(动态链接器)")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("ncmds")])]),t._v(" "),a("td",[a("code",[t._v("Load Commands")]),t._v(" 的数量")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("sizeofcmds")])]),t._v(" "),a("td",[a("code",[t._v("Load Commands")]),t._v(" 所占的总字节大小")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("flags")])]),t._v(" "),a("td",[t._v("一些标识信息,可在 "),a("code",[t._v("mach-o/loader.h")]),t._v(" 中找到具体定义和取值。"),a("br"),t._v("其中 "),a("code",[t._v("#define MH_PIE 0x200000")]),t._v(" 值得注意,只会在文件类型为 "),a("code",[t._v("MH_EXECUTE")]),t._v(" 时使用,表明开启 "),a("code",[t._v("ASLR")]),t._v(",用来增加程序安全性。")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("reserved")])]),t._v(" "),a("td",[t._v("系统保留字段")])])])]),t._v(" "),a("p",[t._v("注: "),a("code",[t._v("ASLR")]),t._v(" ,全称 "),a("code",[t._v("Address Space Layout Randomization")]),t._v(",地址空间布局随机化,顾名思义,每次启动程序,加载的地址都会随机变化,需要对代码地址进行计算修正才可正常访问。")]),t._v(" "),a("h3",{attrs:{id:"load-commands"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#load-commands"}},[t._v("#")]),t._v(" Load Commands")]),t._v(" "),a("blockquote",[a("p",[t._v("加载命令("),a("code",[t._v("Load Commands")]),t._v(")紧跟 "),a("code",[t._v("Header")]),t._v("之后,指定了文件的逻辑结构与文件在虚拟内存中的布局,明确地告诉加载器如何处理二进制数据。有些命令由内核处理,有些由动态链接器("),a("code",[t._v("dyld")]),t._v(")处理。")])]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112261636453.png",alt:"Load Commands"}})]),t._v(" "),a("p",[a("code",[t._v("Load Commands")]),t._v(" 可以当作是多个 "),a("code",[t._v("command")]),t._v(" 的集合,每一个 "),a("code",[t._v("command")]),t._v(" 的类型 "),a("code",[t._v("cmd")]),t._v(" 都是以 "),a("code",[t._v("LC_")]),t._v(" 为前缀的常量,如 "),a("code",[t._v("LC_SEGMENT")]),t._v("。")]),t._v(" "),a("p",[t._v("在头文件 "),a("code",[t._v("mach-o/loader.h")]),t._v(" 中可以查看每个 "),a("code",[t._v("command")]),t._v(" 的定义,每个 "),a("code",[t._v("command")]),t._v(" 都拥有自己的独立结构,但是其结构的前两个字段固定为 "),a("code",[t._v("cmd")]),t._v(" 和 "),a("code",[t._v("cmdsize")])]),t._v(" "),a("div",{staticClass:"language-c extra-class"},[a("pre",{pre:!0,attrs:{class:"language-c"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("load_command")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v(" cmd"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* type of load command */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v(" cmdsize"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* total size of command in bytes */")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("table",[a("thead",[a("tr",[a("th",[t._v("字段")]),t._v(" "),a("th",[t._v("作用")])])]),t._v(" "),a("tbody",[a("tr",[a("td",[a("code",[t._v("cmd")])]),t._v(" "),a("td",[t._v("当前 "),a("code",[t._v("Load Commands")]),t._v(" 的类型,如 "),a("code",[t._v("LC_SEGMENT")])])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("cmdsize")])]),t._v(" "),a("td",[t._v("当前 "),a("code",[t._v("Load Commands")]),t._v(" 的大小,保证其可被正确解析")])])])]),t._v(" "),a("p",[t._v("根据不同的命令类型("),a("code",[t._v("cmd")]),t._v("),内核会使用不同的函数进行解析。")]),t._v(" "),a("p",[t._v("下面对几个重要的命令类型进行详解。")]),t._v(" "),a("h4",{attrs:{id:"lc-segment"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#lc-segment"}},[t._v("#")]),t._v(" "),a("code",[t._v("LC_SEGMENT")])]),t._v(" "),a("p",[a("code",[t._v("LC_SEGMENT")]),t._v(" 和 "),a("code",[t._v("LC_SEGMENT_64")]),t._v(" 为段加载命令,每个段都定义了一个虚拟内存区域,动态链接器负责把这个区域映射到进程地址空间。其结构定义如下所示:")]),t._v(" "),a("div",{staticClass:"language-c extra-class"},[a("pre",{pre:!0,attrs:{class:"language-c"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("segment_command_64")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* for 64-bit architectures */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v("\tcmd"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* LC_SEGMENT_64 */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v("\tcmdsize"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* includes sizeof section_64 structs */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("char")]),t._v("\tsegname"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[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("\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* segment name */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint64_t")]),t._v("\tvmaddr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* memory address of this segment */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint64_t")]),t._v("\tvmsize"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* memory size of this segment */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint64_t")]),t._v("\tfileoff"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* file offset of this segment */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint64_t")]),t._v("\tfilesize"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* amount to map from the file */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("vm_prot_t")]),t._v("\tmaxprot"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* maximum VM protection */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("vm_prot_t")]),t._v("\tinitprot"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* initial VM protection */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v("\tnsects"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* number of sections in segment */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v("\tflags"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* flags */")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("table",[a("thead",[a("tr",[a("th",[t._v("字段")]),t._v(" "),a("th",[t._v("描述")])])]),t._v(" "),a("tbody",[a("tr",[a("td",[a("code",[t._v("cmd")])]),t._v(" "),a("td",[t._v("当前 "),a("code",[t._v("command")]),t._v(" 的类型")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("cmdsize")])]),t._v(" "),a("td",[t._v("当前 "),a("code",[t._v("command")]),t._v(" 的大小")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("segname")])]),t._v(" "),a("td",[t._v("段名称,占16个字节")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("vmaddr")])]),t._v(" "),a("td",[t._v("段的虚拟内存地址")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("vmsize")])]),t._v(" "),a("td",[t._v("段的虚拟内存大小")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("fileoff")])]),t._v(" "),a("td",[t._v("段在文件中的偏移量")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("filesize")])]),t._v(" "),a("td",[t._v("段在文件中的大小")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("maxprot")])]),t._v(" "),a("td",[t._v("段页面的最高内存保护级别")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("initprot")])]),t._v(" "),a("td",[t._v("段页面的初始内存保护级别")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("nsects")])]),t._v(" "),a("td",[t._v("段中包含节的数量。一个段可以包含0个或多个节")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("flags")])]),t._v(" "),a("td",[t._v("段的标志信息("),a("code",[t._v("SG_HIGHVM")]),t._v("、"),a("code",[t._v("SG_FVMLIB")]),t._v("等)")])])])]),t._v(" "),a("p",[t._v("系统从 "),a("code",[t._v("fileoff")]),t._v(" 处加载大小为 "),a("code",[t._v("filesize")]),t._v(" 的内容到虚拟内存 "),a("code",[t._v("vmaddr")]),t._v(" 处,大小为 "),a("code",[t._v("vmsize")]),t._v(", 段页面的权限由 "),a("code",[t._v("initprot")]),t._v(" 进行初始化,权限可被修改,但不可超过 "),a("code",[t._v("maxprot")]),t._v(" 的值。")]),t._v(" "),a("p",[t._v("上图中的四个段作用如下:")]),t._v(" "),a("table",[a("thead",[a("tr",[a("th",[t._v("段")]),t._v(" "),a("th",[t._v("描述")])])]),t._v(" "),a("tbody",[a("tr",[a("td",[a("code",[t._v("__PAGEZERO")])]),t._v(" "),a("td",[t._v("静态链接器创建了 "),a("code",[t._v("__PAGEZERO")]),t._v(" 作为可执行文件的第一个段,该段在虚拟内存中的位置和大小皆为 "),a("code",[t._v("0")]),t._v(",不能读写、不能执行,用来处理空指针。")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("__TEXT")])]),t._v(" "),a("td",[t._v("包含了可执行的代码和其他一些只读数据。静态链接器设置该段的虚拟内存权限为可读、可执行,进程被允许执行这些代码,但不能修改。")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("__DATA")])]),t._v(" "),a("td",[t._v("包含了将会被更改的数据。静态链接器设置该段的虚拟内存权限为可读写。")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("__LINKEDIT")])]),t._v(" "),a("td",[t._v("包含了动态链接库的原始数据,如符号、字符串和重定位表条目等。")])])])]),t._v(" "),a("p",[a("code",[t._v("64")]),t._v(" 位的节("),a("code",[t._v("section")]),t._v(")结构定义:")]),t._v(" "),a("div",{staticClass:"language-c extra-class"},[a("pre",{pre:!0,attrs:{class:"language-c"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("section_64")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* for 64-bit architectures */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("char")]),t._v("\tsectname"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[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("\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* name of this section */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("char")]),t._v("\tsegname"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[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("\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* segment this section goes in */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint64_t")]),t._v("\taddr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* memory address of this section */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint64_t")]),t._v("\tsize"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* size in bytes of this section */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v("\toffset"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* file offset of this section */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v("\talign"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* section alignment (power of 2) */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v("\treloff"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* file offset of relocation entries */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v("\tnreloc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* number of relocation entries */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v("\tflags"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* flags (section type and attributes)*/")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v("\treserved1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* reserved (for offset or index) */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v("\treserved2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* reserved (for count or sizeof) */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v("\treserved3"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* reserved */")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("table",[a("thead",[a("tr",[a("th",[t._v("段")]),t._v(" "),a("th",[t._v("描述")])])]),t._v(" "),a("tbody",[a("tr",[a("td",[a("code",[t._v("sectname")])]),t._v(" "),a("td",[t._v("节的名称,占 "),a("code",[t._v("16")]),t._v(" 个字节")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("segname")])]),t._v(" "),a("td",[t._v("节指导的段名称,占 "),a("code",[t._v("16")]),t._v(" 个字节")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("addr")])]),t._v(" "),a("td",[t._v("节在内存中的起始位置")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("size")])]),t._v(" "),a("td",[t._v("节占用的内存大小")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("offset")])]),t._v(" "),a("td",[t._v("节的文件偏移地址")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("align")])]),t._v(" "),a("td",[t._v("节的字节对齐大小")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("reloff")])]),t._v(" "),a("td",[t._v("重定位入口的文件偏移")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("nreloc")])]),t._v(" "),a("td",[t._v("需要重定位的入口数量")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("flags")])]),t._v(" "),a("td",[t._v("节的类型和属性")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("reserved1/2/3")])]),t._v(" "),a("td",[t._v("系统保留字段")])])])]),t._v(" "),a("h4",{attrs:{id:"lc-load-dylib"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#lc-load-dylib"}},[t._v("#")]),t._v(" "),a("code",[t._v("LC_LOAD_DYLIB")])]),t._v(" "),a("p",[a("code",[t._v("LC_LOAD_DYLIB")]),t._v(" 指向程序依赖库的加载信息,可以使用 "),a("code",[t._v("MachOView")]),t._v(" 进行查看")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112261637245.png",alt:"LC_LOAD_DYLIB"}})]),t._v(" "),a("p",[a("code",[t._v("LC_LOAD_DYLIB")]),t._v(" 的结构定义为 "),a("code",[t._v("dylib_command")])]),t._v(" "),a("div",{staticClass:"language-c extra-class"},[a("pre",{pre:!0,attrs:{class:"language-c"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("dylib")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("union")]),t._v(" lc_str name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* library's path name */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v(" timestamp"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* library's build time stamp */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v(" current_version"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* library's current version number */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v(" compatibility_version"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* library's compatibility vers number*/")]),t._v("\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("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("dylib_command")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v("\t\tcmd"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* LC_ID_DYLIB, LC_LOAD_{,WEAK_}DYLIB, LC_REEXPORT_DYLIB */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v("\t\tcmdsize"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* includes pathname string */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("dylib")]),t._v("\tdylib"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* the library identification */")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("table",[a("thead",[a("tr",[a("th",[t._v("字段")]),t._v(" "),a("th",[t._v("描述")])])]),t._v(" "),a("tbody",[a("tr",[a("td",[a("code",[t._v("name")])]),t._v(" "),a("td",[t._v("依赖库的完整路径。动态链接器会使用此路径进行动态库加载")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("timestamp")])]),t._v(" "),a("td",[t._v("依赖库构建时的时间戳")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("current_version")])]),t._v(" "),a("td",[t._v("当前版本号")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("compatibility_version")])]),t._v(" "),a("td",[t._v("兼容版本号")])])])]),t._v(" "),a("p",[a("code",[t._v("LC_LOAD_WEAK_DYLIB")]),t._v(" 的结构也是 "),a("code",[t._v("dylib_command")]),t._v(",不同的是其声明的依赖库是可选的,即缺少声明的依赖库不会影响主程序的运行,而 "),a("code",[t._v("LC_LOAD_DYLIB")]),t._v(" 声明的依赖库如果找不到,加载器会放弃并结束进程。")]),t._v(" "),a("p",[t._v("可以使用 "),a("code",[t._v("otool")]),t._v(" 来查看有哪些依赖库")]),t._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[t._v("otool -arch arm64 -L LXFProtocolTool_Example\n\nLXFProtocolTool_Example:\n /System/Library/Frameworks/Accelerate.framework/Accelerate (compatibility version 1.0.0, current version 4.0.0)\n @rpath/Alamofire.framework/Alamofire (compatibility version 1.0.0, current version 1.0.0)\n /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)\n /usr/lib/swift/libswiftCoreMIDI.dylib (compatibility version 1.0.0, current version 5.0.0, weak)\n ...\n")])])]),a("p",[t._v("除了 "),a("code",[t._v("/System/Library/")]),t._v(" 和 "),a("code",[t._v("/usr/lib")]),t._v(" 这些系统路径外,还可能会遇到 "),a("code",[t._v("@rpath")]),t._v("、"),a("code",[t._v("@executable_path")]),t._v(" 之类的路径")]),t._v(" "),a("table",[a("thead",[a("tr",[a("th",[t._v("路径")]),t._v(" "),a("th",[t._v("描述")])])]),t._v(" "),a("tbody",[a("tr",[a("td",[a("code",[t._v("@executable_path")])]),t._v(" "),a("td",[t._v("指可执行文件的目录")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("@rpath")])]),t._v(" "),a("td",[t._v("由 "),a("code",[t._v("LC_RPATH")]),t._v(" 加载指定指定,"),a("code",[t._v("iOS")]),t._v(" 上通常为应用自身 "),a("code",[t._v("framework")]),t._v(" 文件,默认为:"),a("code",[t._v("@executable_path/Framework")])])])])]),t._v(" "),a("p",[t._v("这些路径可使用 "),a("code",[t._v("MacOS")]),t._v(" 上提供的 "),a("code",[t._v("install_name_tool")]),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 comment"}},[t._v("# 修改依赖库路径")]),t._v("\ninstall_name_tool "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-change")]),t._v(" @rpath/Alamofire.framework/Alamofire @executable_path/Alamofire.framework/Alamofire LXFProtocolTool_Example\n")])])]),a("h2",{attrs:{id:"通用二进制"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#通用二进制"}},[t._v("#")]),t._v(" 通用二进制")]),t._v(" "),a("p",[a("code",[t._v("Universal Binary")]),t._v("格式文件(通用二进制,也称胖二进制),实际上只是将不同架构的的 "),a("code",[t._v("Mach-O")]),t._v(" 文件打包到一起,再在文件起始位置处加上 "),a("code",[t._v("fat_header")]),t._v(" 结构来说明所支持的架构和偏移地址信息,其结构如下图所示:")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202112261638987.png",alt:""}})]),t._v(" "),a("p",[t._v("头文件 "),a("code",[t._v("mach-o/fat.h")]),t._v(" 中可查看通用二进制文件的定义:")]),t._v(" "),a("div",{staticClass:"language-c extra-class"},[a("pre",{pre:!0,attrs:{class:"language-c"}},[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("define")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token macro-name"}},[t._v("FAT_MAGIC")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token expression"}},[a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0xcafebabe")])])]),t._v("\n"),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("define")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token macro-name"}},[t._v("FAT_CIGAM")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token expression"}},[a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0xbebafeca")]),t._v(" ")]),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* NXSwapLong(FAT_MAGIC) */")])]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("fat_header")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v(" magic"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* FAT_MAGIC or FAT_MAGIC_64 */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v(" nfat_arch"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* number of structs that follow */")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("table",[a("thead",[a("tr",[a("th",[t._v("字段")]),t._v(" "),a("th",[t._v("作用")])])]),t._v(" "),a("tbody",[a("tr",[a("td",[a("code",[t._v("magic")])]),t._v(" "),a("td",[t._v("魔数(特征字段),其被定义常量 "),a("code",[t._v("FAT_MAGIC")]),t._v(",即固定取值为 "),a("code",[t._v("0xcafebabe")])])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("nfat_arch")])]),t._v(" "),a("td",[t._v("标识 "),a("code",[t._v("Mach-O")]),t._v(" 文件包含的架构个数")])])])]),t._v(" "),a("p",[a("code",[t._v("fat_header")]),t._v(" 后紧跟 "),a("code",[t._v("fat_arch")]),t._v(" 结构,有多少架构就会有多少 "),a("code",[t._v("fat_arch")]),t._v(",用于描述对应的 "),a("code",[t._v("Mach-O")]),t._v("文件的具体信息")]),t._v(" "),a("div",{staticClass:"language-c extra-class"},[a("pre",{pre:!0,attrs:{class:"language-c"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("fat_arch")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("cpu_type_t")]),t._v(" cputype"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* cpu specifier (int) */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("cpu_subtype_t")]),t._v(" cpusubtype"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* machine specifier (int) */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v(" offset"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* file offset to this object file */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v(" size"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* size of this object file */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("uint32_t")]),t._v(" align"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* alignment as a power of 2 */")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("table",[a("thead",[a("tr",[a("th",[t._v("字段")]),t._v(" "),a("th",[t._v("作用")])])]),t._v(" "),a("tbody",[a("tr",[a("td",[a("code",[t._v("offset")])]),t._v(" "),a("td",[t._v("指定对应架构相对于文件开头的偏移量")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("size")])]),t._v(" "),a("td",[t._v("指定对应架构数据的大小")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("align")])]),t._v(" "),a("td",[t._v("指定数据的内存对齐边界,取舍为 "),a("code",[t._v("2")]),t._v(" 的 "),a("code",[t._v("N")]),t._v(" 次方")])])])]),t._v(" "),a("p",[a("code",[t._v("cputype")]),t._v(" 和 "),a("code",[t._v("cpusubtype")]),t._v(" 在前面已经提及过,这里就不赘述了")]),t._v(" "),a("h2",{attrs:{id:"资料"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#资料"}},[t._v("#")]),t._v(" 资料")]),t._v(" "),a("ul",[a("li",[t._v("xnu源码\n"),a("ul",[a("li",[a("a",{attrs:{href:"https://github.com/apple/darwin-xnu/",target:"_blank",rel:"noopener noreferrer"}},[t._v("github"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://opensource.apple.com/tarballs/xnu/",target:"_blank",rel:"noopener noreferrer"}},[t._v("opensource.apple.com"),a("OutboundLink")],1)])])]),t._v(" "),a("li",[a("a",{attrs:{href:"https://github.com/aidansteele/osx-abi-macho-file-format-reference",target:"_blank",rel:"noopener noreferrer"}},[t._v("《OS X ABI Mach-O File Format Reference》"),a("OutboundLink")],1)])])])}),[],!1,null,null,null);a.default=_.exports}}]); \ No newline at end of file diff --git a/assets/js/111.8eded375.js b/assets/js/111.8eded375.js new file mode 100644 index 000000000..377408bc3 --- /dev/null +++ b/assets/js/111.8eded375.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[111],{424:function(a,e,t){"use strict";t.r(e);var s=t(8),r=Object(s.a)({},(function(){var a=this,e=a._self._c;return e("ContentSlotsDistributor",{attrs:{"slot-key":a.$parent.slotKey}},[e("blockquote",[e("p",[e("code",[a._v("iOS")]),a._v(" 端 "),e("code",[a._v("APP")]),a._v(" 在上线之前,会经过苹果商店进行 "),e("code",[a._v("FairPlayDRM")]),a._v(" 数字版本加密保护,俗称 “加壳”,如果想对应用进行分析,就必须进行 “脱壳”,从而得到未加密的二进制文件")])]),a._v(" "),e("h2",{attrs:{id:"一、检测是否加壳"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#一、检测是否加壳"}},[a._v("#")]),a._v(" 一、检测是否加壳")]),a._v(" "),e("p",[a._v("介绍两个常用的方式")]),a._v(" "),e("h3",{attrs:{id:"_1、使用-otool-检测"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_1、使用-otool-检测"}},[a._v("#")]),a._v(" 1、使用 "),e("code",[a._v("otool")]),a._v(" 检测")]),a._v(" "),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("otool "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[a._v("-l")]),a._v(" Twitter "),e("span",{pre:!0,attrs:{class:"token operator"}},[a._v("|")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[a._v("grep")]),a._v(" crypt\n")])])]),e("p",[a._v("在得到的结果中,如果 "),e("code",[a._v("cryptid")]),a._v(" 的值为 "),e("code",[a._v("1")]),a._v(",则说明已加壳,如果为 "),e("code",[a._v("0")]),a._v(" 则说明未加壳")]),a._v(" "),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202202191513156.png",alt:""}})]),a._v(" "),e("h3",{attrs:{id:"_2、使用-machoview-检测"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_2、使用-machoview-检测"}},[a._v("#")]),a._v(" 2、使用 "),e("code",[a._v("MachOView")]),a._v(" 检测")]),a._v(" "),e("p",[a._v("使用 "),e("code",[a._v("MachOView")]),a._v("打开目标文件后,展开 "),e("code",[a._v("Load Commands")]),a._v(" 找到 "),e("code",[a._v("LC_ENCRYPTION_INFO_64")]),a._v(",右侧可以看到 "),e("code",[a._v("Crypt ID")]),a._v(",同上,"),e("code",[a._v("1")]),a._v(" 为已加壳,"),e("code",[a._v("0")]),a._v(" 为未加壳")]),a._v(" "),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202202191513318.png",alt:""}})]),a._v(" "),e("h2",{attrs:{id:"二、脱壳"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#二、脱壳"}},[a._v("#")]),a._v(" 二、脱壳")]),a._v(" "),e("p",[a._v("以下罗列常用的脱壳工具,推荐度从上到下依次递减")]),a._v(" "),e("h3",{attrs:{id:"_1、crackerxi"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_1、crackerxi"}},[a._v("#")]),a._v(" 1、"),e("code",[a._v("CrackerXI")])]),a._v(" "),e("blockquote",[e("p",[e("code",[a._v("CrackerXI")]),a._v(" 的安装和使用都较为简单,是目前为止最为傻瓜式的脱壳工具")])]),a._v(" "),e("p",[a._v("在 "),e("code",[a._v("Cydia")]),a._v(" 中添加源地址 "),e("code",[a._v("http://cydia.iphonecake.com/")]),a._v(",添加完成后搜索 "),e("code",[a._v("CrackerXI")]),a._v(",找到后安装即可。")]),a._v(" "),e("p",[a._v("在进行脱壳前,需要进入 "),e("code",[a._v("Settings")]),a._v(" 页面进行配置,配置如下图所示")]),a._v(" "),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202202191513574.png",alt:""}})]),a._v(" "),e("p",[a._v("切回 "),e("code",[a._v("AppList")]),a._v(" 页面,点击需要脱壳的 "),e("code",[a._v("App")]),a._v(",在弹出的对话框中选择 "),e("code",[a._v("YES,Full IPA")]),a._v(" 即可。")]),a._v(" "),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202202191514703.png",alt:""}})]),a._v(" "),e("h3",{attrs:{id:"_2、clutch"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_2、clutch"}},[a._v("#")]),a._v(" 2、"),e("code",[a._v("Clutch")])]),a._v(" "),e("p",[a._v("https://github.com/KJCracks/Clutch")]),a._v(" "),e("p",[a._v("在 "),e("code",[a._v("Release")]),a._v(" 页面找到最新版本的可执行文件,下载后改名为 "),e("code",[a._v("Clutch")]),a._v(",执行下方命令,将 "),e("code",[a._v("Cluth")]),a._v(" 文件复制到 "),e("code",[a._v("iOS")]),a._v(" 设备的 "),e("code",[a._v("/usr/bin")]),a._v(" 目录下")]),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 function"}},[a._v("scp")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[a._v("-P")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[a._v("2222")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[a._v("-r")]),a._v(" ./Clutch root@localhost:/usr/bin/\n")])])]),e("p",[a._v("登录到 "),e("code",[a._v("iOS")]),a._v(" 设备,给 "),e("code",[a._v("Cluth")]),a._v(" 文件添加执行权限")]),a._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[a._v("lxf-iPad:~ root"),e("span",{pre:!0,attrs:{class:"token comment"}},[a._v("# chmod +x /usr/bin/Clutch")]),a._v("\n")])])]),e("p",[a._v("至于怎么通过端口 "),e("code",[a._v("2222")]),a._v(" 就可以从 "),e("code",[a._v("Mac")]),a._v(" 上传输给 "),e("code",[a._v("iOS")]),a._v(" 设备,以及如何登录到 "),e("code",[a._v("iOS")]),a._v(" 设备,请查看本人的另一篇文章:"),e("a",{attrs:{href:"https://juejin.cn/post/7042980829999398920",target:"_blank",rel:"noopener noreferrer"}},[a._v("Mac远程登录到iOS设备"),e("OutboundLink")],1)]),a._v(" "),e("p",[a._v("执行 "),e("code",[a._v("Clutch")]),a._v(" 可查看帮助信息")]),a._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[a._v("lxf-iPad:~ root"),e("span",{pre:!0,attrs:{class:"token comment"}},[a._v("# Clutch")]),a._v("\nUsage: Clutch "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("[")]),a._v("OPTIONS"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("]")]),a._v("\n"),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[a._v("-b")]),a._v(" --binary-dump "),e("span",{pre:!0,attrs:{class:"token operator"}},[a._v("<")]),a._v("value"),e("span",{pre:!0,attrs:{class:"token operator"}},[a._v(">")]),a._v(" Only dump binary files from specified bundleID\n"),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[a._v("-d")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[a._v("--dump")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[a._v("<")]),a._v("value"),e("span",{pre:!0,attrs:{class:"token operator"}},[a._v(">")]),a._v(" Dump specified bundleID into .ipa "),e("span",{pre:!0,attrs:{class:"token function"}},[a._v("file")]),a._v("\n"),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[a._v("-i")]),a._v(" --print-installed Print installed applications\n "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[a._v("--clean")]),a._v(" Clean /var/tmp/clutch directory\n "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[a._v("--version")]),a._v(" Display version and "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[a._v("exit")]),a._v("\n-? "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[a._v("--help")]),a._v(" Display this "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[a._v("help")]),a._v(" and "),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[a._v("exit")]),a._v("\n"),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[a._v("-n")]),a._v(" --no-color Print with colors disabled\n")])])]),e("p",[e("code",[a._v("-i")]),a._v(" 参数可以打印从 "),e("code",[a._v("AppStore")]),a._v(" 上下载安装的 "),e("code",[a._v("App")])]),a._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[a._v("lxf-iPad:~ root"),e("span",{pre:!0,attrs:{class:"token comment"}},[a._v("# Clutch -i")]),a._v("\nInstalled apps:\n"),e("span",{pre:!0,attrs:{class:"token number"}},[a._v("1")]),a._v(": Twitter "),e("span",{pre:!0,attrs:{class:"token operator"}},[a._v("<")]),a._v("com.atebits.Tweetie"),e("span",{pre:!0,attrs:{class:"token operator"}},[e("span",{pre:!0,attrs:{class:"token file-descriptor important"}},[a._v("2")]),a._v(">")]),a._v("\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("..")]),a._v(".\n")])])]),e("p",[e("code",[a._v("-d")]),a._v(" 参数可以进行脱壳,这里以 "),e("code",[a._v("Twitter")]),a._v(" 为例")]),a._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[a._v("Clutch "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[a._v("-d")]),a._v(" com.atebits.Tweetie2\n")])])]),e("p",[a._v("脱壳后的 "),e("code",[a._v("ipa")]),a._v(" 文件会存放至 "),e("code",[a._v("/private/var/mobile/Documents/Dumped/")]),a._v(" 目录下")]),a._v(" "),e("h3",{attrs:{id:"_3、dumpdecrypted"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_3、dumpdecrypted"}},[a._v("#")]),a._v(" 3、"),e("code",[a._v("dumpdecrypted")])]),a._v(" "),e("p",[a._v("https://github.com/stefanesser/dumpdecrypted")]),a._v(" "),e("p",[a._v("下载源码,在 "),e("code",[a._v("dumpdecrypted")]),a._v(" 目录下执行 "),e("code",[a._v("make")]),a._v(" 命令编译生成 "),e("code",[a._v("dumpdecrypted.dylib")])]),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 comment"}},[a._v("# 进入 dumpdecrypted 目录")]),a._v("\n"),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[a._v("cd")]),a._v(" xxx/dumpdecrypted\n\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[a._v("# 进行编译")]),a._v("\n"),e("span",{pre:!0,attrs:{class:"token function"}},[a._v("make")]),a._v("\n")])])]),e("p",[a._v("编译完成后,目录下就有了 "),e("code",[a._v("dumpdecrypted.dylib")]),a._v(" 文件")]),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(".")]),a._v("\n├── Makefile\n├── README\n├── dumpdecrypted.c\n├── dumpdecrypted.dylib\n└── dumpdecrypted.o\n")])])]),e("p",[a._v("将 "),e("code",[a._v("dumpdecrypted.dylib")]),a._v(" 文件复制 "),e("code",[a._v("iOS")]),a._v(" 设备")]),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 function"}},[a._v("scp")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[a._v("-P")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[a._v("2222")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[a._v("-r")]),a._v(" ./dumpdecrypted.dylib root@localhost:/usr/bin/\n")])])]),e("p",[a._v("使用 "),e("code",[a._v("ps")]),a._v(" 命令查看目标文件的完整路径")]),a._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[a._v("lxf-iPad:~ root"),e("span",{pre:!0,attrs:{class:"token comment"}},[a._v("# ps -ax | grep Twitter")]),a._v("\n"),e("span",{pre:!0,attrs:{class:"token number"}},[a._v("15153")]),a._v(" ?? "),e("span",{pre:!0,attrs:{class:"token number"}},[a._v("0")]),a._v(":00.00 /var/containers/Bundle/Application/3A7C3480-C30B-40D7-A0A4-6C313E42B793/Twitter.app/Twitter\n"),e("span",{pre:!0,attrs:{class:"token number"}},[a._v("15161")]),a._v(" ttys000 "),e("span",{pre:!0,attrs:{class:"token number"}},[a._v("0")]),a._v(":00.02 "),e("span",{pre:!0,attrs:{class:"token function"}},[a._v("grep")]),a._v(" Twitter\n")])])]),e("p",[a._v("使用 "),e("code",[a._v("DYLD_INSERT_LIBRARIES")]),a._v(" 环境变量将 "),e("code",[a._v("dumpdecrypted.dylib")]),a._v(" 注入就可以脱壳。")]),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 assign-left variable"}},[a._v("DYLD_INSERT_LIBRARIES")]),e("span",{pre:!0,attrs:{class:"token operator"}},[a._v("=")]),a._v("dumpdecrypted.dylib /var/containers/Bundle/Application/3A7C3480-C30B-40D7-A0A4-6C313E42B793/Twitter.app/Twitter\n")])])]),e("p",[a._v("执行完成后会在将脱壳后的文件存放到当前目录下")]),a._v(" "),e("h3",{attrs:{id:"_4、frida-ios-dump"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_4、frida-ios-dump"}},[a._v("#")]),a._v(" 4、frida-ios-dump")]),a._v(" "),e("p",[e("a",{attrs:{href:"https://github.com/AloneMonkey/frida-ios-dump",target:"_blank",rel:"noopener noreferrer"}},[a._v("https://github.com/AloneMonkey/frida-ios-dump"),e("OutboundLink")],1)]),a._v(" "),e("p",[a._v("下载源码后,安装其 "),e("code",[a._v("python")]),a._v(" 脚本所需依赖包")]),a._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[a._v("pip "),e("span",{pre:!0,attrs:{class:"token function"}},[a._v("install")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[a._v("-r")]),a._v(" requirements.tx\n")])])]),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202202191514736.png",alt:""}})]),a._v(" "),e("p",[e("code",[a._v("-h")]),a._v(" 参数查看帮助")]),a._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[a._v("python dump.py "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[a._v("-h")]),a._v("\n")])])]),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202202191514875.png",alt:""}})]),a._v(" "),e("p",[e("code",[a._v("-l")]),a._v(" 参数查看本机已应用 "),e("code",[a._v("App")])]),a._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[a._v("./dump.py "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[a._v("-l")]),a._v("\n")])])]),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202202191514754.png",alt:""}})]),a._v(" "),e("h2",{attrs:{id:"三、取出脱壳文件"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#三、取出脱壳文件"}},[a._v("#")]),a._v(" 三、取出脱壳文件")]),a._v(" "),e("h3",{attrs:{id:"_1、爱思助手"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_1、爱思助手"}},[a._v("#")]),a._v(" 1、爱思助手")]),a._v(" "),e("p",[a._v("以 "),e("code",[a._v("CrackerXI")]),a._v(" 为例,按此路径便可找到脱壳后的文件")]),a._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[a._v("/private/var/mobile/Documents/CrackerXI/\n")])])]),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202202191514617.png",alt:""}})]),a._v(" "),e("p",[a._v("找到后选中目的文件,点击左上角的 "),e("code",[a._v("导出")]),a._v(" 按钮,或者右击文件,在弹出的菜单中选中 "),e("code",[a._v("导出")]),a._v(" 按钮即可。")]),a._v(" "),e("h3",{attrs:{id:"_2、scp"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_2、scp"}},[a._v("#")]),a._v(" 2、SCP")]),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 function"}},[a._v("scp")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[a._v("-P")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[a._v("2222")]),a._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[a._v("-r")]),a._v(" root@localhost:/private/var/mobile/Documents/CrackerXI/Twitter_8.61_LXF.ipa ./\n")])])]),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202202191515748.png",alt:""}})]),a._v(" "),e("h2",{attrs:{id:"四、验证"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#四、验证"}},[a._v("#")]),a._v(" 四、验证")]),a._v(" "),e("p",[a._v("正如文章开头所说的,使用 "),e("code",[a._v("otool")]),a._v(" 或 "),e("code",[a._v("MachOView")]),a._v(" 查看 "),e("code",[a._v("cryptid")]),a._v(" 的值,为 "),e("code",[a._v("0")]),a._v(" 即为脱壳成功。")]),a._v(" "),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202202191515813.png",alt:""}})]),a._v(" "),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202202191515142.png",alt:""}})])])}),[],!1,null,null,null);e.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/112.0b4e5a43.js b/assets/js/112.0b4e5a43.js new file mode 100644 index 000000000..d0ded7792 --- /dev/null +++ b/assets/js/112.0b4e5a43.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[112],{423:function(s,a,t){"use strict";t.r(a);var e=t(8),r=Object(e.a)({},(function(){var s=this,a=s._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[a("h2",{attrs:{id:"概述"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#概述"}},[s._v("#")]),s._v(" 概述")]),s._v(" "),a("p",[s._v("众所周知,"),a("code",[s._v("OC")]),s._v(" 是一门面向对象的动态编程语言,在程序运行时才会确定对象的真实类型,并调用类和对象的相应方法。所以在 "),a("code",[s._v("iOS")]),s._v(" 逆向领域便出现了一些工具,利用 "),a("code",[s._v("Runtime")]),s._v(" 帮助我们开发者在程序运行时去动态修改类和对象中的属性和方法。")]),s._v(" "),a("h2",{attrs:{id:"class-dump"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#class-dump"}},[s._v("#")]),s._v(" "),a("code",[s._v("class-dump")])]),s._v(" "),a("p",[a("code",[s._v("class-dump")]),s._v(" 是一个命令行工具,可对存储在 "),a("code",[s._v("Mach-O")]),s._v(" 文件中的 "),a("code",[s._v("OC")]),s._v(" "),a("code",[s._v("Runtime")]),s._v(" 信息进行检测,生成类、分类和协议的属性和方法的声明,即 "),a("code",[s._v(".h")]),s._v(" 头文件。")]),s._v(" "),a("h3",{attrs:{id:"_1、下载安装"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_1、下载安装"}},[s._v("#")]),s._v(" 1、下载安装")]),s._v(" "),a("p",[s._v("官网下载链接:")]),s._v(" "),a("ul",[a("li",[a("p",[a("a",{attrs:{href:"http://stevenygard.com/projects/class-dump/",target:"_blank",rel:"noopener noreferrer"}},[s._v("http://stevenygard.com/projects/class-dump"),a("OutboundLink")],1)])]),s._v(" "),a("li",[a("p",[a("a",{attrs:{href:"https://github.com/nygard/class-dump",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://github.com/nygard/class-dump"),a("OutboundLink")],1)])])]),s._v(" "),a("p",[s._v("下载后解压,将 "),a("code",[s._v("class-dump")]),s._v(" 拖到 "),a("code",[s._v("/usr/local/bin")]),s._v(" 目录下即可在终端内使用")]),s._v(" "),a("p",[s._v("官网下载的 "),a("code",[s._v("class-dump")]),s._v(" 由于很久没有维护了,不支持 "),a("code",[s._v("Swift")]),s._v(" 和 "),a("code",[s._v("OC")]),s._v(" 混编的二进制文件,会报如下错误")]),s._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[s._v("class-dump"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("36848")]),s._v(":11809395"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" Error: Cannot "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("find")]),s._v(" offset "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("for")]),s._v(" address 0x800000001001586 "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("in")]),s._v(" stringAtAddress:\n")])])]),a("p",[s._v("所以推荐从 "),a("a",{attrs:{href:"https://github.com/AloneMonkey/MonkeyDev/blob/master/bin/class-dump",target:"_blank",rel:"noopener noreferrer"}},[s._v("https://github.com/AloneMonkey/MonkeyDev/blob/master/bin/class-dump"),a("OutboundLink")],1),s._v(" 下载 "),a("code",[s._v("class-dump")])]),s._v(" "),a("p",[s._v("下载后拖到 "),a("code",[s._v("/usr/local/bin")]),s._v(" 目录下,再赋予执行权限方可在终端内使用")]),s._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"}},[s._v("chmod")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("755")]),s._v(" /usr/local/bin/class-dump\n")])])]),a("h3",{attrs:{id:"_2、使用"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_2、使用"}},[s._v("#")]),s._v(" 2、使用")]),s._v(" "),a("p",[s._v("使用参数说明:")]),s._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[s._v("class-dump "),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("3.5")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[s._v("64")]),s._v(" bit"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\nUsage: class-dump "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("options"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("mach-o-file"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v("\n\n where options are:\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("-a")]),s._v(" show instance variable offsets\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("-A")]),s._v(" show implementation addresses\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("--arch")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("arch"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" choose a specific architecture from a universal binary "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("ppc, ppc64, i386, x86_64"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("-C")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("regex"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" only display classes matching regular expression\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("-f")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("str"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("find")]),s._v(" string "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("in")]),s._v(" method name\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("-H")]),s._v(" generate header files "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("in")]),s._v(" current directory, or directory specified with "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("-o")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("-I")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("sort")]),s._v(" classes, categories, and protocols by inheritance "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("overrides -s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("-o")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("dir"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(" output directory used "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("for")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("-H")]),s._v("\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("-r")]),s._v(" recursively "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("expand")]),s._v(" frameworks and fixed VM shared libraries\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("-s")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("sort")]),s._v(" classes and categories by name\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("-S")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("sort")]),s._v(" methods by name\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("-t")]),s._v(" suppress header "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("in")]),s._v(" output, "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("for")]),s._v(" testing\n --list-arches list the arches "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("in")]),s._v(" the file, "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("then")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("exit")]),s._v("\n --sdk-ios specify iOS SDK version "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("will "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("look")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("in")]),s._v(" /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("version"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(".sdk\n --sdk-mac specify Mac OS X version "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("will "),a("span",{pre:!0,attrs:{class:"token function"}},[s._v("look")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("in")]),s._v(" /Developer/SDKs/MacOSX"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v("<")]),s._v("version"),a("span",{pre:!0,attrs:{class:"token operator"}},[s._v(">")]),s._v(".sdk\n --sdk-root specify the full SDK root path "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("(")]),s._v("or use --sdk-ios/--sdk-mac "),a("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("for")]),s._v(" a shortcut"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(")")]),s._v("\n")])])]),a("p",[s._v("举个例子:我们对 "),a("code",[s._v("Twitter")]),s._v(" 的二进制文件导出头文件")]),s._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[s._v("class-dump "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("--arch")]),s._v(" arm64 "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("-a")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("-A")]),s._v(" "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("-H")]),s._v(" Twitter "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("-o")]),s._v(" ./Headers\n")])])]),a("p",[s._v("各参数说明:")]),s._v(" "),a("table",[a("thead",[a("tr",[a("th",[s._v("参数")]),s._v(" "),a("th",[s._v("作用")])])]),s._v(" "),a("tbody",[a("tr",[a("td",[a("code",[s._v("a")])]),s._v(" "),a("td",[s._v("显示实例变量的偏移地址")])]),s._v(" "),a("tr",[a("td",[a("code",[s._v("A")])]),s._v(" "),a("td",[s._v("显示方法实现地址")])]),s._v(" "),a("tr",[a("td",[a("code",[s._v("H")])]),s._v(" "),a("td",[s._v("生成头文件")])]),s._v(" "),a("tr",[a("td",[a("code",[s._v("arch")])]),s._v(" "),a("td",[s._v("指定架构(如果只有一个架构,可以不加该参数)")])]),s._v(" "),a("tr",[a("td",[a("code",[s._v("o")])]),s._v(" "),a("td",[s._v("指定保存生成的头文件的目录位置")])])])]),s._v(" "),a("p",[s._v("执行完成后,可以看到 "),a("code",[s._v("Headers")]),s._v(" 目录下一共有 "),a("code",[s._v("343")]),s._v(" 个文件")]),s._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202203191623479.png",alt:"Headers"}})]),s._v(" "),a("p",[s._v("任意打开一个头文件,可以看到变量名、方法名、偏移地址等信息")]),s._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202203191623825.png",alt:""}})])])}),[],!1,null,null,null);a.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/113.8ac5df7a.js b/assets/js/113.8ac5df7a.js new file mode 100644 index 000000000..dedc4fa4b --- /dev/null +++ b/assets/js/113.8ac5df7a.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[113],{425:function(t,a,s){"use strict";s.r(a);var e=s(8),n=Object(e.a)({},(function(){var t=this,a=t._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h2",{attrs:{id:"一、cycript"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#一、cycript"}},[t._v("#")]),t._v(" 一、"),a("code",[t._v("Cycript")])]),t._v(" "),a("p",[a("code",[t._v("Cycript")]),t._v(" 是一款混合了 "),a("code",[t._v("OC")]),t._v(" 和 "),a("code",[t._v("JS")]),t._v(" 语法解释器的脚本语言(即可以使用 "),a("code",[t._v("OC")]),t._v(" 和 "),a("code",[t._v("JS")]),t._v(" 的语法来写脚本),其主要用来注入运行时程序进行调试,在程序重启后,所有通过 "),a("code",[t._v("Cycript")]),t._v(" 进行的程序修改内容都会失效,对原生程序和代码毫无副作用。")]),t._v(" "),a("p",[t._v("官网地址:"),a("a",{attrs:{href:"http://www.cycript.org/",target:"_blank",rel:"noopener noreferrer"}},[t._v("http://www.cycript.org"),a("OutboundLink")],1)]),t._v(" "),a("h2",{attrs:{id:"二、安装与简单使用"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#二、安装与简单使用"}},[t._v("#")]),t._v(" 二、安装与简单使用")]),t._v(" "),a("p",[t._v("在 "),a("code",[t._v("Cydia")]),t._v(" 中搜索 "),a("code",[t._v("Cycript")]),t._v(",找到安装即可。")]),t._v(" "),a("p",[t._v("在 "),a("code",[t._v("iOS")]),t._v(" 设备上输入 "),a("code",[t._v("cycript")]),t._v(",会显示 "),a("code",[t._v("cy#")]),t._v(" 提示符")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("lxf-iPad:~ root"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# cycript")]),t._v("\ncy"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("#")]),t._v("\n")])])]),a("p",[t._v("这是一个 "),a("code",[t._v("JS")]),t._v(" 控制台,输入的内容都由 "),a("code",[t._v("JS")]),t._v(" 内核运行。")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("cy# "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),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("'lxf'")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"lxf"')]),t._v("\ncy# "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("print")]),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("\nlxf\n")])])]),a("p",[t._v("常用快捷键")]),t._v(" "),a("table",[a("thead",[a("tr",[a("th",[t._v("快捷键")]),t._v(" "),a("th",[t._v("作用")])])]),t._v(" "),a("tbody",[a("tr",[a("td",[a("code",[t._v("ctrl + c")])]),t._v(" "),a("td",[t._v("取消当前输入或中断执行中的命令")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("ctrl + d")])]),t._v(" "),a("td",[t._v("退出 "),a("code",[t._v("Cycript")])])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("ctrl + l")])]),t._v(" "),a("td",[a("code",[t._v("clear")]),t._v(" 清屏")])])])]),t._v(" "),a("p",[t._v("使用 "),a("code",[t._v("-p")]),t._v(" 参数注入指定程序,前提是你的程序得在运行中。")]),t._v(" "),a("p",[t._v("下面展示一下注入 "),a("code",[t._v("Twitter")]),t._v(" 后,查看其沙盒路径")]),t._v(" "),a("div",{staticClass:"language-objc extra-class"},[a("pre",{pre:!0,attrs:{class:"language-objc"}},[a("code",[t._v("lxf"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("iPad"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("~")]),t._v(" root# cycript "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("p Twitter\ncy# "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("NSHomeDirectory")]),a("span",{pre:!0,attrs:{class:"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('@"/var/mobile/Containers/Data/Application/2FA6975C-9DB0-4B08-9FE9-365473E86748"')]),t._v("\ncy#\n")])])]),a("h2",{attrs:{id:"三、实战"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#三、实战"}},[t._v("#")]),t._v(" 三、实战")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202203191629839.png",alt:""}})]),t._v(" "),a("blockquote",[a("p",[t._v("目标:如上图所示,我们来将 "),a("code",[t._v("linxunfeng.top")]),t._v(" 改成 "),a("code",[t._v("fullstackaction.com")])])]),t._v(" "),a("p",[t._v("要修改它就得先找到它,可以使用 "),a("code",[t._v("recursiveDescription")]),t._v(" 函数可以递归打印任意视图的层次结构")]),t._v(" "),a("div",{staticClass:"language-objc extra-class"},[a("pre",{pre:!0,attrs:{class:"language-objc"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("UIApp keyWindow"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" recursiveDescription"),a("span",{pre:!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("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("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202203191630427.png",alt:""}})]),t._v(" "),a("p",[t._v("如果我们不清楚完整的函数名时,可以使用敲击 "),a("code",[t._v("tab")]),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 comment"}},[t._v("# cy# [[UIApp keyWindow] recursive【敲击tab键】")]),t._v("\n\ncy"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# [[UIApp keyWindow] recursive")]),t._v("\nrecursiveDescription recursivelyForceDisplayIfNeeded\n")])])]),a("p",[t._v("看着上面图中打印出来的内容,任谁看了都觉得很烦恼,这里可以使用简化版的方法 "),a("code",[t._v("_autolayoutTrace")])]),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 punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("UIApp keyWindow"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" _autolayoutTrace"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),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("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202203191630533.png",alt:""}})]),t._v(" "),a("p",[t._v("相比上面的 "),a("code",[t._v("recursiveDescription")]),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",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("u2022T1ProfileHeaderViewContro"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".:0x1164bac50\n UIView:0x1164bae30\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" T1ProfileUserInfoViewCont"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".:0x117748050\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ProfileHeaderBio:0x116498070\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" T1TranslateButton:0x116474cc0\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ProfileHeaderTranslatedBi"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".:0x11773ed70\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ProfileTranslationActivit"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".:0x11773f170\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" UIImageView:0x11773f3f0\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ProfileHeaderLocation:0x11641f090\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" UIButtonLabel:0x11641f3b0\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ProfileHeaderWebSite:0x113526840\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" UIImageView:0x1164e3b60\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" UIButtonLabel:0x11355bbe0"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'linxunfeng.top'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ProfileHeaderBirthday:0x11355bee0\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" UIButtonLabel:0x11355c200\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ProfileHeaderCreatedDate:0x11355c500\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" UIImageView:0x1164e4100\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" UIButtonLabel:0x11355c820"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'2017\\u5e748\\u6708 \\u52a0\\u5165'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" TFNAttributedTextView:0x11641f6b0\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" T1AccessibilityProxyView:0x11641af00\n T1ProfileFriendsFollowing"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".:0x1164dc870\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" TFNSolidColorView:0x1164dca50\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" T1FlexibleLayoutView:0x1135df410\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" TFNAttributedTextView:0x1135df810\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),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(" T1AccessibilityProxyView:0x1164e06a0\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" T1UserFacepileView:0x1164df730\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" TFNAttributedTextView:0x1164dfb50\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" T1ProfileFriendsFollowingHighlightView:0x1164dff50\n")])])]),a("p",[t._v("这里说明一下,"),a("code",[t._v("Cycript")]),t._v(" 对中文的显示不太友好")]),t._v(" "),a("p",[t._v("可以将上面的输出内容拷贝到一些 "),a("code",[t._v("Unicode转中文")]),t._v(" 的工具里,直接转换输出")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("•T1ProfileHeaderViewContro"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".:0x1164bac50\n UIView:0x1164bae30\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" T1ProfileUserInfoViewCont"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".:0x117748050\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ProfileHeaderBio:0x116498070\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" T1TranslateButton:0x116474cc0\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ProfileHeaderTranslatedBi"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".:0x11773ed70\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ProfileTranslationActivit"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".:0x11773f170\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" UIImageView:0x11773f3f0\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ProfileHeaderLocation:0x11641f090\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" UIButtonLabel:0x11641f3b0\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ProfileHeaderWebSite:0x113526840\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" UIImageView:0x1164e3b60\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" UIButtonLabel:0x11355bbe0"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'linxunfeng.top'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ProfileHeaderBirthday:0x11355bee0\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" UIButtonLabel:0x11355c200\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ProfileHeaderCreatedDate:0x11355c500\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" UIImageView:0x1164e4100\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" UIButtonLabel:0x11355c820"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'2017年8月 加入'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" TFNAttributedTextView:0x11641f6b0\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" T1AccessibilityProxyView:0x11641af00\n T1ProfileFriendsFollowing"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".:0x1164dc870\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" TFNSolidColorView:0x1164dca50\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" T1FlexibleLayoutView:0x1135df410\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" TFNAttributedTextView:0x1135df810\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),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(" T1AccessibilityProxyView:0x1164e06a0\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" T1UserFacepileView:0x1164df730\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" TFNAttributedTextView:0x1164dfb50\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" T1ProfileFriendsFollowingHighlightView:0x1164dff50\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("UIButtonLabel:0x11355c820"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'2017\\u5e748\\u6708 \\u52a0\\u5165'")]),t._v("\n")])])]),a("p",[t._v("可以使用 "),a("code",[t._v("echo -e")]),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("echo")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-e")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'2017\\u5e748\\u6708 \\u52a0\\u5165'")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2017")]),t._v("年8月 加入\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("UIButtonLabel:0x11355bbe0"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'linxunfeng.top'")]),t._v("\n")])])]),a("blockquote",[a("p",[t._v("在 "),a("code",[t._v("Cycript")]),t._v(" 中,如果我们知道一个对象的内存地址,就可以通过 "),a("code",[t._v("#")]),t._v(" 操作符来获取该对象。")])]),t._v(" "),a("p",[t._v("不过它的类型是:"),a("code",[t._v("UIButtonLabel")]),t._v(",我们可以看看它的父控件")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ProfileHeaderWebSite:0x113526840\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" UIImageView:0x1164e3b60\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" UIButtonLabel:0x11355bbe0"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'linxunfeng.top'")]),t._v("\n")])])]),a("p",[t._v("打印 "),a("code",[t._v("ProfileHeaderWebSite:0x113526840")]),t._v(" 这个对象")]),t._v(" "),a("div",{staticClass:"language-objc extra-class"},[a("pre",{pre:!0,attrs:{class:"language-objc"}},[a("code",[t._v("cy# #"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x113526840")]),t._v("\n#"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"; layer = >"')]),t._v("\n")])])]),a("p",[t._v("可以看到是个 "),a("code",[t._v("UIButton")]),t._v(",那怎么改标题不是很明白了吗?")]),t._v(" "),a("div",{staticClass:"language-objc extra-class"},[a("pre",{pre:!0,attrs:{class:"language-objc"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("#"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x113526840")]),t._v(" setTitle"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('@"fullstackaction.com"')]),t._v(" forState"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("UIControlStateNormal"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n")])])]),a("p",[t._v("但是并没有任何效果~,没事,那就换设置富文本试试")]),t._v(" "),a("div",{staticClass:"language-objc extra-class"},[a("pre",{pre:!0,attrs:{class:"language-objc"}},[a("code",[t._v("var siteAttr "),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("NSAttributedString alloc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" initWithString"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('@"fullstackaction.com"')]),t._v(" attributes"),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("NSFontAttributeName"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("UIFont systemFontOfSize"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("NSForegroundColorAttributeName"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("UIColor blueColor"),a("span",{pre:!0,attrs:{class:"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\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("#"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x113526840")]),t._v(" setAttributedTitle"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("siteAttr forState"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("UIControlStateNormal"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n")])])]),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202203191630565.png",alt:""}})]),t._v(" "),a("p",[t._v("大功告成😃")]),t._v(" "),a("h2",{attrs:{id:"四、高级用法"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#四、高级用法"}},[t._v("#")]),t._v(" 四、高级用法")]),t._v(" "),a("h3",{attrs:{id:"_1、choose-查找对象"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_1、choose-查找对象"}},[t._v("#")]),t._v(" 1、"),a("code",[t._v("choose")]),t._v(" - 查找对象")]),t._v(" "),a("p",[t._v("用法:"),a("code",[t._v("choose(类名)")])]),t._v(" "),a("p",[t._v("有时候上面提到的 "),a("code",[t._v("recursiveDescription")]),t._v(" 和 "),a("code",[t._v("_autolayoutTrace")]),t._v(",对我们来说输出内容太多了,且我们知道目标对象的类名,此时就可以通过 "),a("code",[t._v("choose")]),t._v(" 这个函数,获取符合条件(当前类及子类)的所有记录,从而快速拿到对象的地址来操作对象")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("cy"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# choose(UILabel)")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("#\"; layer = <_UILabelLayer: 0x2803585a0>>\",#\">\",#\">\",#\">\",#\">\",#\"; layer = <_UILabelLayer: 0x28035d9f0>>\"]")]),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("在 "),a("code",[t._v("Cycript")]),t._v(" 中也可以使用 "),a("code",[t._v("OC")]),t._v(" 的 "),a("code",[t._v("Category")]),t._v(",为类拓展方法,如")]),t._v(" "),a("div",{staticClass:"language-objc extra-class"},[a("pre",{pre:!0,attrs:{class:"language-objc"}},[a("code",[t._v("cy# "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("@implementation")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("NSString")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("LXF"),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(" website "),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 string"}},[t._v('"fullstackaction.com"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[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("@end")]),t._v("\n")])])]),a("div",{staticClass:"language-objc extra-class"},[a("pre",{pre:!0,attrs:{class:"language-objc"}},[a("code",[t._v("cy# "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("NSString website"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('@"fullstackaction.com"')]),t._v("\n")])])]),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("Cycript")]),t._v(" 来编写自定义函数解决这个问题")]),t._v(" "),a("div",{staticClass:"language-objc extra-class"},[a("pre",{pre:!0,attrs:{class:"language-objc"}},[a("code",[t._v("cy# function "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("topVc")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("vc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[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("vc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("presentedViewController"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[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("_topVc")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("vc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("presentedViewController"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("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("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("vc isKindOfClass"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("UITabBarController class"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[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("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("_topVc")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("vc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("selectedViewController"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("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("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("vc isKindOfClass"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("UINavigationController class"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[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("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("_topVc")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("vc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("visibleViewController"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("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 var count "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" vc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("childViewControllers"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count"),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("var i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" count "),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(" "),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("--")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n var childVc "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" vc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("childViewControllers"),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("\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("childVc "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" childVc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("view"),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(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n vc "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("_topVc")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("childVc"),a("span",{pre:!0,attrs:{class:"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("break")]),a("span",{pre:!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 keyword"}},[t._v("return")]),t._v(" vc"),a("span",{pre:!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-objc extra-class"},[a("pre",{pre:!0,attrs:{class:"language-objc"}},[a("code",[t._v("cy# "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("topVc")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("UIApp"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("keyWindow"),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#"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),t._v("\n")])])]),a("p",[t._v("每次都在终端里写一次,那岂不是也非常麻烦?我们可以将这些代码写到一个 "),a("code",[t._v(".cy")]),t._v(" 文件中")]),t._v(" "),a("p",[t._v("我们可以在 "),a("code",[t._v("/usr/lib/cycript0.9/com/saurik/substrate/")]),t._v(" 找到一个名为 "),a("code",[t._v("MS.cy")]),t._v(" 的文件")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("exports")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\nexports"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("getImageByName "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" MSGetImageByName"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nexports"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("findSymbol "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" MSFindSymbol"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\nexports"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function-variable function"}},[t._v("hookFunction")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("func"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" hook"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" old")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),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("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\nexports"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function-variable function"}},[t._v("hookMessage")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("isa"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" sel"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" imp"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" old")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),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("\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 punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("exports"),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("cy")]),t._v(" 文件:"),a("a",{attrs:{href:"https://github.com/LinXunFeng/lxf_cycript",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/LinXunFeng/lxf_cycript"),a("OutboundLink")],1)]),t._v(" "),a("p",[t._v("下载下来后,将 "),a("code",[t._v("lxf.cy")]),t._v(" 拷贝至 "),a("code",[t._v("/usr/lib/cycript0.9/com/lxf/")]),t._v(" 路径下")]),t._v(" "),a("p",[t._v("然后在 "),a("code",[t._v("cycript")]),t._v(" 环境下执行:")]),t._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[t._v("cy# @import com.lxf.lxf; 0\n")])])]),a("p",[t._v("注:分号后面的"),a("code",[t._v("0")]),t._v(" 是为了隐藏脚本导入后的脚本内容输出")]),t._v(" "),a("p",[t._v("导入后,使用 "),a("code",[t._v("lxf.函数名()")]),t._v(" 即可")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("cy# lxf"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("topVc")]),a("span",{pre:!0,attrs:{class:"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('""')]),t._v("\n")])])]),a("p",[t._v("如果不太记得函数名字,可以使用 "),a("code",[t._v("tab键")]),t._v(" 查看")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("cy# lxf"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("【敲击tab键】\n__defineGetter__ __lookupSetter__ appPath classMethods findVc isString keyWindow methods rootVc toLocaleString valueOf\n__defineSetter__ __proto__ cachesPath constructor hasOwnProperty ivarNames loadFramework printIvars subViews toString\n__lookupGetter__ appId classMethodNames documentPath isPrototypeOf ivars methodNames propertyIsEnumerable subViewsSimple topVc\ncy# lxf"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\n")])])])])}),[],!1,null,null,null);a.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/114.e0f5ab9c.js b/assets/js/114.e0f5ab9c.js new file mode 100644 index 000000000..e8f752d1e --- /dev/null +++ b/assets/js/114.e0f5ab9c.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[114],{426:function(t,a,s){"use strict";s.r(a);var n=s(8),e=Object(n.a)({},(function(){var t=this,a=t._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h2",{attrs:{id:"一、简介"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#一、简介"}},[t._v("#")]),t._v(" 一、简介")]),t._v(" "),a("p",[a("code",[t._v("Frida")]),t._v(" 是一个跨平台的轻量级 "),a("code",[t._v("Hook")]),t._v(" 框架,支持 "),a("code",[t._v("MacOS")]),t._v("、"),a("code",[t._v("Linux")]),t._v(" 和 "),a("code",[t._v("Windows")]),t._v(" 操作系统,提供了精简的 "),a("code",[t._v("Python")]),t._v(" 接口和功能丰富的 "),a("code",[t._v("JS")]),t._v(" 接口,除了可以使用自身的控制台交互以外,还可以使用 "),a("code",[t._v("Python")]),t._v(" 将 "),a("code",[t._v("JS")]),t._v(" 脚本注入到运行程序中,通过 "),a("code",[t._v("Frida")]),t._v(" 可以获取程序的详细信息、拦截和调用指定函数、注入代码、修改参数等。")]),t._v(" "),a("p",[a("code",[t._v("Frida")]),t._v(" 源代码托管:"),a("a",{attrs:{href:"https://github.com/frida",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/frida"),a("OutboundLink")],1)]),t._v(" "),a("h2",{attrs:{id:"二、安装"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#二、安装"}},[t._v("#")]),t._v(" 二、安装")]),t._v(" "),a("h3",{attrs:{id:"ios"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#ios"}},[t._v("#")]),t._v(" "),a("code",[t._v("iOS")])]),t._v(" "),a("p",[t._v("添加软件源 "),a("code",[t._v("https://build.frida.re/")]),t._v(",然后搜索 "),a("code",[t._v("Frida")]),t._v(" 安装即可。")]),t._v(" "),a("p",[t._v("安装完成后可能通过 "),a("code",[t._v("ps")]),t._v(" 看到 "),a("code",[t._v("frida-server")]),t._v(" 后台程序则说明安装成功,若没有可以重启手机后再看看")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("lxf-iPad:~ root"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# ps -ax | grep frida")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("26717")]),t._v(" ?? "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(":00.08 /usr/sbin/frida-server\n"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("26731")]),t._v(" ttys000 "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(":00.01 "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("grep")]),t._v(" frida\n")])])]),a("h3",{attrs:{id:"macos"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#macos"}},[t._v("#")]),t._v(" "),a("code",[t._v("MacOS")])]),t._v(" "),a("p",[t._v("使用 "),a("code",[t._v("pip")]),t._v(" 进行安装")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("pip "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("install")]),t._v(" frida-tools "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# CLI tools")]),t._v("\npip "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("install")]),t._v(" frida "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Python bindings")]),t._v("\n")])])]),a("p",[t._v("后续需要升级的话,可以使用 "),a("code",[t._v("--upgrade")]),t._v(" 参数")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("pip "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("install")]),t._v(" frida-tools "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--upgrade")]),t._v("\npip "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("install")]),t._v(" frida "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--upgrade")]),t._v("\n")])])]),a("p",[t._v("如果报 "),a("code",[t._v("command not found:pip")]),t._v(" 错误,说明当前系统没有安装 "),a("code",[t._v("pip")]),t._v(",可以使用下方命令安装")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("brew "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("install")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("wget")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("wget")]),t._v(" https://bootstrap.pypa.io/get-pip.py\npython3 get-pip.py\n")])])]),a("p",[t._v("执行完成后会提示你将对应 "),a("code",[t._v("python")]),t._v(" 版本的 "),a("code",[t._v("bin")]),t._v(" 路径添加到 "),a("code",[t._v("PATH")]),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("=~")]),t._v("/Library/Python/3.8/bin:"),a("span",{pre:!0,attrs:{class:"token environment constant"}},[t._v("$PATH")]),t._v("\n")])])]),a("p",[t._v("当然,如果你也有用 "),a("code",[t._v("pyenv")]),t._v(",则可以忽略上述的 "),a("code",[t._v("pip")]),t._v(" 安装流程,因为 "),a("code",[t._v("pyenv")]),t._v(" 自带了 "),a("code",[t._v("pip")]),t._v("。")]),t._v(" "),a("p",[t._v("你可以用 "),a("code",[t._v("which")]),t._v(" 命令查看你的 "),a("code",[t._v("pip")]),t._v(" 的安装位置。")]),t._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[t._v("➜ ~ which pip\n/usr/local/var/pyenv/shims/pip\n")])])]),a("h2",{attrs:{id:"三、入门"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#三、入门"}},[t._v("#")]),t._v(" 三、入门")]),t._v(" "),a("p",[t._v("除了 "),a("code",[t._v("frida")]),t._v(" 主程序外,"),a("code",[t._v("frida-tools")]),t._v(" 里还提供了五个实用工具,它们位于 "),a("code",[t._v("/usr/local/bin/")]),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 function"}},[t._v("ls")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-al")]),t._v(" /usr/local/bin/frida-*\n")])])]),a("p",[t._v("如果使用的是 "),a("code",[t._v("pyenv")]),t._v(" 的 "),a("code",[t._v("pip")]),t._v(" 安装的 "),a("code",[t._v("frida")]),t._v(",则它们会被安装到 "),a("code",[t._v("/usr/local/var/pyenv/shims/")]),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 function"}},[t._v("ls")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-al")]),t._v(" /usr/local/var/pyenv/shims/frida-*\n")])])]),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("-rwxr-xr-x "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" lxf admin "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("180")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("19")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("22")]),t._v(":05 /usr/local/var/pyenv/shims/frida-apk\n-rwxr-xr-x "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" lxf admin "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("180")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("19")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("22")]),t._v(":05 /usr/local/var/pyenv/shims/frida-create\n-rwxr-xr-x "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" lxf admin "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("180")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("19")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("22")]),t._v(":05 /usr/local/var/pyenv/shims/frida-discover\n-rwxr-xr-x "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" lxf admin "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("180")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("19")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("22")]),t._v(":05 /usr/local/var/pyenv/shims/frida-join\n-rwxr-xr-x "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" lxf admin "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("180")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("19")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("22")]),t._v(":05 /usr/local/var/pyenv/shims/frida-kill\n-rwxr-xr-x "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" lxf admin "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("180")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("19")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("22")]),t._v(":05 /usr/local/var/pyenv/shims/frida-ls-devices\n-rwxr-xr-x "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" lxf admin "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("180")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("19")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("22")]),t._v(":05 /usr/local/var/pyenv/shims/frida-ps\n-rwxr-xr-x "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" lxf admin "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("180")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("19")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("22")]),t._v(":05 /usr/local/var/pyenv/shims/frida-trace\n")])])]),a("h3",{attrs:{id:"_1、查看可用的设备列表"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_1、查看可用的设备列表"}},[t._v("#")]),t._v(" 1、查看可用的设备列表")]),t._v(" "),a("p",[a("code",[t._v("frida-ls-devices")]),t._v(" 用于获取可用的设备列表,在多设备交互的情况下会非常有用")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("➜ ~ frida-ls-devices\nId Type Name\n---------------------------------------- ------ ------------\n"),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("local")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("local")]),t._v(" Local System\nd007dc58edd70caad950ff01b41ebf73cfa49fbe usb iPad\nsocket remote Local Socket\n")])])]),a("h3",{attrs:{id:"_2、获取设备的进程列表"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_2、获取设备的进程列表"}},[t._v("#")]),t._v(" 2、获取设备的进程列表")]),t._v(" "),a("p",[a("code",[t._v("frida-ps")]),t._v(" 用于获取进程列表信息")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("➜ ~ frida-ps "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--help")]),t._v("\nusage: frida-ps "),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\noptions:\n -h, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--help")]),t._v(" show this "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("help")]),t._v(" message and "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("exit")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-D")]),t._v(" ID, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--device")]),t._v(" ID connect to device with the given ID\n -U, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--usb")]),t._v(" connect to USB device\n -R, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--remote")]),t._v(" connect to remote frida-server\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-H")]),t._v(" HOST, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--host")]),t._v(" HOST connect to remote frida-server on HOST\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--certificate")]),t._v(" CERTIFICATE\n speak TLS with HOST, expecting CERTIFICATE\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--origin")]),t._v(" ORIGIN connect to remote server with “Origin” header "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" to\n ORIGIN\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--token")]),t._v(" TOKEN authenticate with HOST using TOKEN\n --keepalive-interval INTERVAL\n "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" keepalive interval "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" seconds, or "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" to disable\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("defaults to "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-1")]),t._v(" to auto-select based on transport"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--p2p")]),t._v(" establish a peer-to-peer connection with target\n --stun-server ADDRESS\n "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" STUN server ADDRESS to use with "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--p2p")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--relay")]),t._v(" address,username,password,turn-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("udp,tcp,tls"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),t._v(" relay to use with "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--p2p")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-O")]),t._v(" FILE, --options-file FILE\n text "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("file")]),t._v(" containing additional "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("command")]),t._v(" line options\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--version")]),t._v(" show program's version number and "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("exit")]),t._v("\n -a, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--applications")]),t._v(" list only applications\n -i, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--installed")]),t._v(" include all installed applications\n -j, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--json")]),t._v(" output results as JSON\n")])])]),a("p",[t._v("这里说明一下常用的命令参数")]),t._v(" "),a("table",[a("thead",[a("tr",[a("th",[t._v("参数")]),t._v(" "),a("th",[t._v("描述")])])]),t._v(" "),a("tbody",[a("tr",[a("td",[a("code",[t._v("-U")])]),t._v(" "),a("td",[t._v("连接到 "),a("code",[t._v("USB")]),t._v(" 设备")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("-D")])]),t._v(" "),a("td",[t._v("如果当前有多台 "),a("code",[t._v("USB")]),t._v(" 设备,可以使用该参数指定设备的 "),a("code",[t._v("UDID")]),t._v("("),a("code",[t._v("frida-ls-devices")]),t._v(" 列出的那些 "),a("code",[t._v("id")]),t._v(")")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("-R/-H")])]),t._v(" "),a("td",[t._v("连接到远程 "),a("code",[t._v("frida-server")]),t._v(",主要用于远程调试")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("-a")])]),t._v(" "),a("td",[t._v("仅显示正在运行的应用")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("-i")])]),t._v(" "),a("td",[t._v("显示所有已安装的应用(包括 "),a("code",[t._v("AppStore")]),t._v("安装的应用和系统应用)")])])])]),t._v(" "),a("p",[t._v("具体使用如下:")]),t._v(" "),a("p",[t._v("连接到 "),a("code",[t._v("USB")]),t._v(" 设备查看进程列表")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("~ frida-ps "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-U")]),t._v("\n PID Name\n----- ---------------------------------------------------\n"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("25226")]),t._v(" Cydia\n"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("26745")]),t._v(" Twitter\n"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("21611")]),t._v(" 邮件\n"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("25055")]),t._v(" AppPredictionWidget\n"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("20944")]),t._v(" AppleCredentialManagerDaemon\n "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1687")]),t._v(" AssetCacheLocatorService\n"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("23387")]),t._v(" CMFSyncAgent\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".\n")])])]),a("p",[t._v("连接到 "),a("code",[t._v("USB")]),t._v(" 设备查看正在运行的应用")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("➜ ~ frida-ps "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-U")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-a")]),t._v("\n PID Name Identifier\n----- ----------- --------------------\n"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("25226")]),t._v(" Cydia com.saurik.Cydia\n"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("26745")]),t._v(" Twitter com.atebits.Tweetie2\n"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("21611")]),t._v(" 邮件 com.apple.mobilemail\n➜ ~\n")])])]),a("p",[t._v("连接到 "),a("code",[t._v("USB")]),t._v(" 设备查看所有安装的应用")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("➜ ~ frida-ps "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-U")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-a")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-i")]),t._v("\n PID Name Identifier\n----- --------------------------- ------------------------------------------\n"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("25226")]),t._v(" Cydia com.saurik.Cydia\n"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("26745")]),t._v(" Twitter com.atebits.Tweetie2\n"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("21611")]),t._v(" 邮件 com.apple.mobilemail\n - App Store com.apple.AppStore\n - FaceTime 通话 com.apple.facetime\n - LXFProtocolTool_Example org.cocoapods.demo.LXFProtocolTool-Example\n - Photo Booth com.apple.Photo-Booth\n - Safari 浏览器 com.apple.mobilesafari\n - Substitute com.ex.substitute.settings\n - SwiftyFitsize_Swift org.cocoapods.demo.SwiftyFitsize-Swift\n - iTunes Store com.apple.MobileStore\n - 信息 com.apple.MobileSMS\n - 查找 iPhone com.apple.mobileme.fmip1\n - 设置 com.apple.Preferences\n"),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("USB")]),t._v(" 设备查看正在运行的应用")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("➜ ~ frida-ps "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-D")]),t._v(" d007dc58edd70caad950ff01b41ebf73cfa49fbe "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-a")]),t._v("\n PID Name Identifier\n----- ----------- --------------------\n"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("25226")]),t._v(" Cydia com.saurik.Cydia\n"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("26745")]),t._v(" Twitter com.atebits.Tweetie2\n"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("21611")]),t._v(" 邮件 com.apple.mobilemail\n➜ ~\n")])])]),a("h3",{attrs:{id:"_3、杀死进程"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_3、杀死进程"}},[t._v("#")]),t._v(" 3、杀死进程")]),t._v(" "),a("p",[a("code",[t._v("frida-kill")]),t._v(" 用来结束设备上的指定进程")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("➜ ~ frida-kill "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--help")]),t._v("\nusage: frida-kill "),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(" process\n\noptions:\n -h, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--help")]),t._v(" show this "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("help")]),t._v(" message and "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("exit")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-D")]),t._v(" ID, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--device")]),t._v(" ID connect to device with the given ID\n -U, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--usb")]),t._v(" connect to USB device\n -R, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--remote")]),t._v(" connect to remote frida-server\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-H")]),t._v(" HOST, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--host")]),t._v(" HOST connect to remote frida-server on HOST\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--certificate")]),t._v(" CERTIFICATE\n speak TLS with HOST, expecting CERTIFICATE\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--origin")]),t._v(" ORIGIN connect to remote server with “Origin” header "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" to\n ORIGIN\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--token")]),t._v(" TOKEN authenticate with HOST using TOKEN\n --keepalive-interval INTERVAL\n "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" keepalive interval "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" seconds, or "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" to disable\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("defaults to "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-1")]),t._v(" to auto-select based on transport"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--p2p")]),t._v(" establish a peer-to-peer connection with target\n --stun-server ADDRESS\n "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" STUN server ADDRESS to use with "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--p2p")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--relay")]),t._v(" address,username,password,turn-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("udp,tcp,tls"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),t._v(" relay to use with "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--p2p")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-O")]),t._v(" FILE, --options-file FILE\n text "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("file")]),t._v(" containing additional "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("command")]),t._v(" line options\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--version")]),t._v(" show program's version number and "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("exit")]),t._v("\n")])])]),a("p",[t._v("举个例子,杀掉 "),a("code",[t._v("PID")]),t._v(" 为 "),a("code",[t._v("26745")]),t._v(" 的 "),a("code",[t._v("Twitter")])]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("frida-kill "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-U")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("26745")]),t._v("\nfrida-kill "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-U")]),t._v(" Twitter\nfrida-kill "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-D")]),t._v(" d007dc58edd70caad950ff01b41ebf73cfa49fbe "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("26745")]),t._v("\nfrida-kill "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-D")]),t._v(" d007dc58edd70caad950ff01b41ebf73cfa49fbe Twitter\n")])])]),a("h3",{attrs:{id:"_4、跟踪函数-方法的调用"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_4、跟踪函数-方法的调用"}},[t._v("#")]),t._v(" 4、跟踪函数/方法的调用")]),t._v(" "),a("p",[a("code",[t._v("frida-trace")]),t._v(" 用于跟踪函数或方法的调用。")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("➜ ~ frida-trace "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--help")]),t._v("\nusage: frida-trace "),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(" target\n\npositional arguments:\n args extra arguments and/or target\n\noptions:\n -h, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--help")]),t._v(" show this "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("help")]),t._v(" message and "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("exit")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-D")]),t._v(" ID, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--device")]),t._v(" ID connect to device with the given ID\n -U, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--usb")]),t._v(" connect to USB device\n -R, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--remote")]),t._v(" connect to remote frida-server\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-H")]),t._v(" HOST, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--host")]),t._v(" HOST connect to remote frida-server on HOST\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--certificate")]),t._v(" CERTIFICATE\n speak TLS with HOST, expecting CERTIFICATE\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--origin")]),t._v(" ORIGIN connect to remote server with “Origin” header "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" to ORIGIN\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--token")]),t._v(" TOKEN authenticate with HOST using TOKEN\n --keepalive-interval INTERVAL\n "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" keepalive interval "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" seconds, or "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" to disable "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("defaults to "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-1")]),t._v(" to auto-select based\n on transport"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--p2p")]),t._v(" establish a peer-to-peer connection with target\n --stun-server ADDRESS\n "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" STUN server ADDRESS to use with "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--p2p")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--relay")]),t._v(" address,username,password,turn-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("udp,tcp,tls"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),t._v(" relay to use with "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--p2p")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" TARGET, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--file")]),t._v(" TARGET\n spawn FILE\n -F, --attach-frontmost\n attach to frontmost application\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-n")]),t._v(" NAME, --attach-name NAME\n attach to NAME\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-p")]),t._v(" PID, --attach-pid PID\n attach to PID\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-W")]),t._v(" PATTERN, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--await")]),t._v(" PATTERN\n await spawn matching PATTERN\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--stdio")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("inherit,pipe"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n stdio behavior when spawning "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("defaults to “inherit”"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--aux")]),t._v(" option "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" aux option when spawning, such as “uid"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("int"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("42")]),t._v("” "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("supported types are: string, bool,\n int"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--realm")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("native,emulated"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n realm to attach "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--runtime")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("qjs,v8"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" script runtime to use\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--debug")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("enable")]),t._v(" the Node.js compatible script debugger\n --squelch-crash "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" enabled, will not dump crash report to console\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-O")]),t._v(" FILE, --options-file FILE\n text "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("file")]),t._v(" containing additional "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("command")]),t._v(" line options\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--version")]),t._v(" show program"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'s version number and exit\n -I MODULE, --include-module MODULE\n include MODULE\n -X MODULE, --exclude-module MODULE\n exclude MODULE\n -i FUNCTION, --include FUNCTION\n include [MODULE!]FUNCTION\n -x FUNCTION, --exclude FUNCTION\n exclude [MODULE!]FUNCTION\n -a MODULE!OFFSET, --add MODULE!OFFSET\n add MODULE!OFFSET\n -T INCLUDE_IMPORTS, --include-imports INCLUDE_IMPORTS\n include program'")]),t._v("s imports\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-t")]),t._v(" MODULE, --include-module-imports MODULE\n include MODULE imports\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-m")]),t._v(" OBJC_METHOD, --include-objc-method OBJC_METHOD\n include OBJC_METHOD\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-M")]),t._v(" OBJC_METHOD, --exclude-objc-method OBJC_METHOD\n exclude OBJC_METHOD\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-j")]),t._v(" JAVA_METHOD, --include-java-method JAVA_METHOD\n include JAVA_METHOD\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-J")]),t._v(" JAVA_METHOD, --exclude-java-method JAVA_METHOD\n exclude JAVA_METHOD\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-s")]),t._v(" DEBUG_SYMBOL, --include-debug-symbol DEBUG_SYMBOL\n include DEBUG_SYMBOL\n -q, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--quiet")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("do")]),t._v(" not "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("format")]),t._v(" output messages\n -d, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--decorate")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),t._v(" module name to generated onEnter log statement\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-S")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token environment constant"}},[t._v("PATH")]),t._v(", --init-session "),a("span",{pre:!0,attrs:{class:"token environment constant"}},[t._v("PATH")]),t._v("\n path to JavaScript "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("file")]),t._v(" used to initialize the session\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-P")]),t._v(" PARAMETERS_JSON, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--parameters")]),t._v(" PARAMETERS_JSON\n parameters as JSON, exposed as a global named "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'parameters'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-o")]),t._v(" OUTPUT, "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--output")]),t._v(" OUTPUT\n dump messages to "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("file")]),t._v("\n")])])]),a("h4",{attrs:{id:"_4-1-跟踪函数调用"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_4-1-跟踪函数调用"}},[t._v("#")]),t._v(" 4.1 跟踪函数调用")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v(" ➜ ~ frida-trace "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-U")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-i")]),t._v(" compress "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-i")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"recv*"')]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-x")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"recvmsg*"')]),t._v(" Twitter\nInstrumenting"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".\ncompress: Auto-generated handler at "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/libz.1.dylib/compress.js"')]),t._v("\nrecvfrom"),a("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$NOCANCEL")]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" Auto-generated handler at "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/libsystem_kernel.dylib/recvfrom_NOCANCEL.js"')]),t._v("\nrecvfrom: Auto-generated handler at "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/libsystem_kernel.dylib/recvfrom.js"')]),t._v("\nrecv: Auto-generated handler at "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/libsystem_c.dylib/recv.js"')]),t._v("\nrecv"),a("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$NOCANCEL")]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" Auto-generated handler at "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/libsystem_c.dylib/recv_NOCANCEL.js"')]),t._v("\nStarted tracing "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),t._v(" functions. Press Ctrl+C to stop.\n")])])]),a("p",[t._v("参数说明")]),t._v(" "),a("table",[a("thead",[a("tr",[a("th",[t._v("参数")]),t._v(" "),a("th",[t._v("描述")])])]),t._v(" "),a("tbody",[a("tr",[a("td",[a("code",[t._v("-i")])]),t._v(" "),a("td",[t._v("包含某个函数,支持模糊匹配")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("-x")])]),t._v(" "),a("td",[t._v("排除某个函数,支持模糊匹配")])])])]),t._v(" "),a("p",[t._v("注:进行模糊匹配时,需要使用双引号进行包裹!")]),t._v(" "),a("p",[t._v("上述命令的意思:跟踪名为 "),a("code",[t._v("compress")]),t._v(" 和以 "),a("code",[t._v("recv")]),t._v(" 开头的函数,且排除以 "),a("code",[t._v("recvmsg")]),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(" /* TID 0x1bb43 */\n "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("36078")]),t._v(" ms recv"),a("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$NOCANCEL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("36078")]),t._v(" ms "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" recvfrom"),a("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$NOCANCEL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("36081")]),t._v(" ms recv"),a("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$NOCANCEL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("36081")]),t._v(" ms "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" recvfrom"),a("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$NOCANCEL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("36082")]),t._v(" ms recv"),a("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$NOCANCEL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("36082")]),t._v(" ms "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" recvfrom"),a("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$NOCANCEL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("36083")]),t._v(" ms recv"),a("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$NOCANCEL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("36083")]),t._v(" ms "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" recvfrom"),a("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$NOCANCEL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("36083")]),t._v(" ms recv"),a("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$NOCANCEL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("36083")]),t._v(" ms "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" recvfrom"),a("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$NOCANCEL")]),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("__handlers__")]),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(".")]),t._v("\n└── __handlers__\n ├── libsystem_c.dylib\n │ ├── recv.js\n │ └── recv_NOCANCEL.js\n ├── libsystem_kernel.dylib\n │ ├── recvfrom.js\n │ └── recvfrom_NOCANCEL.js\n └── libz.1.dylib\n └── compress.js\n")])])]),a("p",[t._v("上述命令是在目标 "),a("code",[t._v("App")]),t._v(" 打开后执行的,如果我们需要强制启动 "),a("code",[t._v("App")]),t._v(" 来进行跟踪,可以使用 "),a("code",[t._v("-f 应用的BundleID")]),t._v(" 参数,如:")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("➜ ~ frida-trace "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-U")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-i")]),t._v(" compress "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-i")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"recv*"')]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-x")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"recvmsg*"')]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"com.atebits.Tweetie2"')]),t._v("\nInstrumenting"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".\ncompress: Loaded handler at "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/libz.1.dylib/compress.js"')]),t._v("\nrecvfrom"),a("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$NOCANCEL")]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" Loaded handler at "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/libsystem_kernel.dylib/recvfrom_NOCANCEL.js"')]),t._v("\nrecvfrom: Loaded handler at "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/libsystem_kernel.dylib/recvfrom.js"')]),t._v("\nrecv: Loaded handler at "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/libsystem_c.dylib/recv.js"')]),t._v("\nrecv"),a("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$NOCANCEL")]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" Loaded handler at "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/libsystem_c.dylib/recv_NOCANCEL.js"')]),t._v("\nStarted tracing "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),t._v(" functions. Press Ctrl+C to stop.\n")])])]),a("p",[t._v("注:"),a("code",[t._v("frida-trace")]),t._v(" 执行时不会覆盖已有的脚本文件(即 "),a("code",[t._v("__handlers__")]),t._v(" 文件夹下的脚本),所以可以进行任意修改这些 "),a("code",[t._v("JS")]),t._v(" 文件来添加想要的功能。")]),t._v(" "),a("h4",{attrs:{id:"_4-2-跟踪-oc-方法的调用"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_4-2-跟踪-oc-方法的调用"}},[t._v("#")]),t._v(" 4.2 跟踪 "),a("code",[t._v("OC")]),t._v(" 方法的调用")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("➜ Test frida-trace "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-U")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-m")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"-[T1HomeTimelineItemsViewController _load*]"')]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-M")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"-[T1HomeTimelineItemsViewController _loadBottomWithSource:]"')]),t._v(" Twitter\nInstrumenting"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1HomeTimelineItemsViewController _loadTopWithSource:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(": Loaded handler at "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/T1HomeTimelineItemsViewController/_loadTopWithSource_.js"')]),t._v("\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1HomeTimelineItemsViewController _loadGap:withSource:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(": Loaded handler at "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/T1HomeTimelineItemsViewController/_loadGap_withSource_.js"')]),t._v("\nStarted tracing "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v(" functions. Press Ctrl+C to stop.\n /* TID 0x303 */\n "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("15600")]),t._v(" ms -"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1HomeTimelineItemsViewController _loadTopWithSource:0xc8"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n")])])]),a("p",[t._v("参数说明")]),t._v(" "),a("table",[a("thead",[a("tr",[a("th",[t._v("参数")]),t._v(" "),a("th",[t._v("描述")])])]),t._v(" "),a("tbody",[a("tr",[a("td",[a("code",[t._v("-m")])]),t._v(" "),a("td",[t._v("包含某个方法,支持模糊匹配")])]),t._v(" "),a("tr",[a("td",[a("code",[t._v("-M")])]),t._v(" "),a("td",[t._v("排除某个方法,支持模糊匹配")])])])]),t._v(" "),a("h4",{attrs:{id:"_4-3-跟踪调用栈"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_4-3-跟踪调用栈"}},[t._v("#")]),t._v(" 4.3 跟踪调用栈")]),t._v(" "),a("p",[t._v("只需要在 "),a("code",[t._v("JS")]),t._v(" 文件中添加如下代码片段即可跟踪某个方法的调用栈")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("console"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("log")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'\\tBacktrace:\\n\\t'")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" Thread"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("backtrace")]),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(",")]),t._v(" Backtracer"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token constant"}},[t._v("ACCURATE")]),a("span",{pre:!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("map")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("DebugSymbol"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("fromAddress"),a("span",{pre:!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 string"}},[t._v("'\\n\\t'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),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("Frida")]),t._v(" 官网链接:"),a("a",{attrs:{href:"https://frida.re/docs/javascript-api/#thread",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://frida.re/docs/javascript-api/#thread"),a("OutboundLink")],1),t._v(" 上找到。")]),t._v(" "),a("h3",{attrs:{id:"_5、交互模式"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_5、交互模式"}},[t._v("#")]),t._v(" 5、交互模式")]),t._v(" "),a("p",[a("code",[t._v("frida")]),t._v(" 提供了两种进入交互模式的方式")]),t._v(" "),a("h4",{attrs:{id:"_5-1-通过应用名或-pid-附加"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_5-1-通过应用名或-pid-附加"}},[t._v("#")]),t._v(" 5.1 通过应用名或 "),a("code",[t._v("PID")]),t._v(" 附加")]),t._v(" "),a("p",[t._v("应用于 "),a("code",[t._v("App")]),t._v(" 已打开的情况下附加的情景")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("frida "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-U")]),t._v(" 应用名\nfrida "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-U")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-p")]),t._v(" PID\n")])])]),a("p",[t._v("当使用 "),a("code",[t._v("PID")]),t._v(" 进行附加时,"),a("code",[t._v("-p")]),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("frida "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-U")]),t._v(" Twitter\nfrida "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-U")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("26984")]),t._v("\nfrida "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-U")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-p")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("26984")]),t._v("\n")])])]),a("h4",{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("应用于 "),a("code",[t._v("App")]),t._v(" 未打开的情景")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("➜ Test frida "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-U")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" com.atebits.Tweetie2\n ____\n / _ "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" Frida "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("15.1")]),t._v(".17 - A world-class dynamic instrumentation toolkit\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("_"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),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(" _ "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" Commands:\n /_/ "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v("_"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("help")]),t._v(" -"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" Displays the "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("help")]),t._v(" system\n "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" object? -"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" Display information about "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'object'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" exit/quit -"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" Exit\n "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" More info at https://frida.re/docs/home/\n "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" Connected to iPad "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("d007dc58edd70caad950ff01b41ebf73cfa49fbe"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nSpawned "),a("span",{pre:!0,attrs:{class:"token variable"}},[a("span",{pre:!0,attrs:{class:"token variable"}},[t._v("`")]),t._v("com.atebits.Tweetie2"),a("span",{pre:!0,attrs:{class:"token variable"}},[t._v("`")])]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" Use %resume to "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("let")]),t._v(" the main thread start executing"),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("iPad::com.atebits.Tweetie2 "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("-"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" %resume\n")])])]),a("p",[t._v("注:需要自己额外再输入 "),a("code",[t._v("%resume")]),t._v(",否则目标应用将一直处于暂停的状态。")]),t._v(" "),a("p",[t._v("如果启动应用后被强制退出或不想再额外输入 "),a("code",[t._v("%resume")]),t._v(",可以加上 "),a("code",[t._v("--no-pause")])]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("frida "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-U")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" com.atebits.Tweetie2 --no-pause\n")])])]),a("h2",{attrs:{id:"四、实战"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#四、实战"}},[t._v("#")]),t._v(" 四、实战")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202203271846280.png",alt:""}})]),t._v(" "),a("blockquote",[a("p",[t._v("针对上图中的【翻译推文】,我们来把这个标题和点击事件给修改掉")])]),t._v(" "),a("p",[t._v("首先我们要做的就是视图组件定位,在这个页面下,使用 "),a("code",[t._v("FLEX")]),t._v(" 工具便可轻松定位到")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202203271846973.png",alt:""}})]),t._v(" "),a("p",[t._v("点击右侧的感叹号,可以看到该视图的属性和方法")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202203271846745.png",alt:""}})]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202203271847821.png",alt:""}})]),t._v(" "),a("p",[t._v("然后通过如下代码,确认其是否为我们想要 "),a("code",[t._v("hook")]),t._v(" 的方法")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[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("ObjC"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("available"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[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(" didTap "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ObjC"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("classes"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("T1TranslateButton"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'- _didTap:forEvent:'")]),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(" setTitle "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ObjC"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("classes"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("T1TranslateButton"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'- setTitleText:'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n Interceptor"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("attach")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("setTitleOldImp"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[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-variable function"}},[t._v("onEnter")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("args")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n console"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("log")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"args 0 -- "')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ObjC"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Object")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("args"),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(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n console"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("log")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"args 2 -- "')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ObjC"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Object")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("args"),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(")")]),a("span",{pre:!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 didTap"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("implementation "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ObjC"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("implement")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("setTitle"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("handle"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" selector"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" arg1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" arg2")]),a("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("var")]),t._v(" self "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ObjC"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Object")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("handle"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n console"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("log")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"self -- "')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" self"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!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("Twitter")]),t._v(" 后,执行如下命令 "),a("code",[t._v("frida -U -l Twitter.js Twitter")])]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("➜ frida "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-U")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-l")]),t._v(" Twitter.js Twitter\n ____\n / _ "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" Frida "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("15.1")]),t._v(".17 - A world-class dynamic instrumentation toolkit\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("_"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),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(" _ "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" Commands:\n /_/ "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v("_"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("help")]),t._v(" -"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" Displays the "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("help")]),t._v(" system\n "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" object? -"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" Display information about "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'object'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" exit/quit -"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" Exit\n "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" More info at https://frida.re/docs/home/\n "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v(" Connected to iPad "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("d007dc58edd70caad950ff01b41ebf73cfa49fbe"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("iPad::Twitter "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("-"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n")])])]),a("p",[a("code",[t._v("Twitter")]),t._v(" 进入到指定页面后输出:")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("args "),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("T1TranslateButton: 0x122e46f80"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" baseClass "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" UIButton"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" frame "),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 number"}},[t._v("0")]),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("66")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("22")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" opaque "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" NO"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" layer "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("CALayer: 0x28372a76"),a("span",{pre:!0,attrs:{class:"token operator"}},[a("span",{pre:!0,attrs:{class:"token file-descriptor important"}},[t._v("0")]),t._v(">>")]),t._v("\nargs "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),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",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("iPad::Twitter "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("-"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" self -- "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("T1TranslateButton: 0x122e46f80"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" baseClass "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" UIButton"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" frame "),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 number"}},[t._v("0")]),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("66")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("22")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" opaque "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" NO"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" layer "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("CALayer: 0x28372a76"),a("span",{pre:!0,attrs:{class:"token operator"}},[a("span",{pre:!0,attrs:{class:"token file-descriptor important"}},[t._v("0")]),t._v(">>")]),t._v("\n")])])]),a("p",[t._v("看来是没错了,那接下来,我们把标题和点击事件进行修改,完整代码如下:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[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("ObjC"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("available"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[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 punctuation"}},[t._v("{")]),t._v(" NSString "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ObjC"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("classes"),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(" UIAlertController "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ObjC"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("classes"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("UIAlertController"),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(" UIAlertAction "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ObjC"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("classes"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("UIAlertAction"),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(" UIApplication "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ObjC"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("classes"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("UIApplication"),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("function")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("showAlert")]),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(" alertHandler "),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("ObjC"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Block")]),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 literal-property property"}},[t._v("retType")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'void'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token literal-property property"}},[t._v("argTypes")]),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 string"}},[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(" "),a("span",{pre:!0,attrs:{class:"token function-variable function"}},[t._v("implementation")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),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("}")]),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 ObjC"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("schedule")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ObjC"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("mainQueue"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),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("var")]),t._v(" alert "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" UIAlertController"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("alertControllerWithTitle_message_preferredStyle_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'LinXunFeng'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'欢迎关注公众号:FSA全栈行动\\n博客:https://fullstackaction.com'")]),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("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" defaultAction "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" UIAlertAction"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("actionWithTitle_style_handler_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'OK'")]),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(" alertHandler"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n alert"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("addAction_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("defaultAction"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n UIApplication"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("sharedApplication")]),a("span",{pre:!0,attrs:{class:"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("keyWindow")]),a("span",{pre:!0,attrs:{class:"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("rootViewController")]),a("span",{pre:!0,attrs:{class:"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("presentViewController_animated_completion_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("alert"),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 constant"}},[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 punctuation"}},[t._v("}")]),a("span",{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("function")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("playSystemSound")]),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(" playSound "),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("NativeFunction")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Module"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("findExportByName")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'AudioToolbox'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'AudioServicesPlaySystemSound'")]),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("'void'")]),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("'int'")]),a("span",{pre:!0,attrs:{class:"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("playSound")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1111")]),a("span",{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("var")]),t._v(" didTap "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ObjC"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("classes"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("T1TranslateButton"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'- _didTap:forEvent:'")]),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(" setTitle "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ObjC"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("classes"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("T1TranslateButton"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'- setTitleText:'")]),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("var")]),t._v(" didTapOldImp "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" didTap"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("implementation\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// hook")]),t._v("\n Interceptor"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("attach")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("setTitleOldImp"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[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-variable function"}},[t._v("onEnter")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("args")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n args"),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("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("ptr")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("NSString"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("stringWithString_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Hello LinXunFeng,点击我来弹个窗和听个曲吧"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!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\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 覆盖实现")]),t._v("\n didTap"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("implementation "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ObjC"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("implement")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("setTitle"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("handle"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" selector"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" arg1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" arg2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[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 comment"}},[t._v("// didTapOldImp(handle, selector, arg1, arg2)")]),t._v("\n \n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("playSystemSound")]),a("span",{pre:!0,attrs:{class:"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("showAlert")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!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("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202203271848750.png",alt:""}})]),t._v(" "),a("h2",{attrs:{id:"五、进阶"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#五、进阶"}},[t._v("#")]),t._v(" 五、进阶")]),t._v(" "),a("h3",{attrs:{id:"_1、python-交互"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_1、python-交互"}},[t._v("#")]),t._v(" 1、"),a("code",[t._v("Python")]),t._v(" 交互")]),t._v(" "),a("p",[a("code",[t._v("Frida")]),t._v(" 提供了 "),a("code",[t._v("Python")]),t._v(" 和 "),a("code",[t._v("JS")]),t._v(" 脚本的交互")]),t._v(" "),a("h4",{attrs:{id:"_1-1、获取设备"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_1-1、获取设备"}},[t._v("#")]),t._v(" 1.1、获取设备")]),t._v(" "),a("div",{staticClass:"language-python extra-class"},[a("pre",{pre:!0,attrs:{class:"language-python"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" frida\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),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("'__main__'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\n deviceManager "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" frida"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get_device_manager"),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("print")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("deviceManager"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("enumerate_devices"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("# 根据 UDID 获取设备")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("deviceManager"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get_device"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"d007dc58edd70caad950ff01b41ebf73cfa49fbe"')]),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("# 获取当前 USB 连接的设备")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("frida"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get_usb_device"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),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-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[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("id"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"local"')]),t._v(", "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("name")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Local System"')]),t._v(", "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("type")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'local'")]),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("id"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"socket"')]),t._v(", "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("name")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Local Socket"')]),t._v(", "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("type")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'remote'")]),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("id"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"d007dc58edd70caad950ff01b41ebf73cfa49fbe"')]),t._v(", "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("name")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"iPad"')]),t._v(", "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("type")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'usb'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\nDevice"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"d007dc58edd70caad950ff01b41ebf73cfa49fbe"')]),t._v(", "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("name")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"iPad"')]),t._v(", "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("type")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'usb'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nDevice"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"d007dc58edd70caad950ff01b41ebf73cfa49fbe"')]),t._v(", "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("name")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"iPad"')]),t._v(", "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("type")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'usb'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("h4",{attrs:{id:"_1-2、附加进程"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_1-2、附加进程"}},[t._v("#")]),t._v(" 1.2、附加进程")]),t._v(" "),a("p",[t._v("使用 "),a("code",[t._v("attach()")]),t._v(" 附加进程,得到 "),a("code",[t._v("Session")]),t._v(" 实例")]),t._v(" "),a("div",{staticClass:"language-python extra-class"},[a("pre",{pre:!0,attrs:{class:"language-python"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),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("'__main__'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n device "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" frida"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get_usb_device"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n session "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" device"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("attach"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Twitter"')]),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("# session = device.attach(27489) # PID")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("session"),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("Session"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("pid"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("27489")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("h4",{attrs:{id:"_1-3、启动进程"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_1-3、启动进程"}},[t._v("#")]),t._v(" 1.3、启动进程")]),t._v(" "),a("p",[t._v("使用 "),a("code",[t._v("spawn")]),t._v(" 可以启动进程,不过会进入挂起状态,需要配合 "),a("code",[t._v("resume()")]),t._v(" 方法才能唤醒")]),t._v(" "),a("div",{staticClass:"language-python extra-class"},[a("pre",{pre:!0,attrs:{class:"language-python"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),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("'__main__'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n device "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" frida"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get_usb_device"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n pid "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" device"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("spawn"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"com.atebits.Tweetie2"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# session = device.attach(pid)")]),t._v("\n device"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("resume"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("pid"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[a("code",[t._v("spawn")]),t._v(" 可携带参数运行")]),t._v(" "),a("p",[t._v("如下方代码所示,运行 "),a("code",[t._v("Safari")]),t._v(" 并打开 "),a("code",[t._v("FSA全栈行动")]),t._v(" 博客: "),a("code",[t._v("https://fullstackaction.com")])]),t._v(" "),a("div",{staticClass:"language-python extra-class"},[a("pre",{pre:!0,attrs:{class:"language-python"}},[a("code",[t._v("pid "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" device"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("spawn"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"com.apple.mobilesafari"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" url"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"https://fullstackaction.com/"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\ndevice"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("resume"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("pid"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("h4",{attrs:{id:"_1-4、脱离进程"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_1-4、脱离进程"}},[t._v("#")]),t._v(" 1.4、脱离进程")]),t._v(" "),a("p",[t._v("得到 "),a("code",[t._v("Session")]),t._v(" 并完成所有操作后,需要使用 "),a("code",[t._v("detach()")]),t._v(" 脱离进程")]),t._v(" "),a("div",{staticClass:"language-python extra-class"},[a("pre",{pre:!0,attrs:{class:"language-python"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),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("'__main__'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n device "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" frida"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get_usb_device"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n pid "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" device"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("spawn"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"com.atebits.Tweetie2"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n session "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" device"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("attach"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("pid"),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("resume"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("pid"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n session"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("detach"),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("h4",{attrs:{id:"_1-5、注入-js-脚本"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_1-5、注入-js-脚本"}},[t._v("#")]),t._v(" 1.5、注入 "),a("code",[t._v("JS")]),t._v(" 脚本")]),t._v(" "),a("p",[t._v("得到 "),a("code",[t._v("Session")]),t._v(" 实例后,就可以调用其 "),a("code",[t._v("create_script")]),t._v(" 方法创建一个脚本对象,再调用该脚本对象的 "),a("code",[t._v("load")]),t._v(" 方法进行脚本注入")]),t._v(" "),a("div",{staticClass:"language-python extra-class"},[a("pre",{pre:!0,attrs:{class:"language-python"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),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("'__main__'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n device "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" frida"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get_usb_device"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n pid "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" device"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("spawn"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"com.atebits.Tweetie2"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n session "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" device"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("attach"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("pid"),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("resume"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("pid"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n script "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" session"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("create_script"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token triple-quoted-string string"}},[t._v('"""\n if (ObjC.available) {\n var NSHomeDirectory = new NativeFunction(ptr(Module.findExportByName("Foundation", "NSHomeDirectory")), \'pointer\', []);\n var path = new ObjC.Object(NSHomeDirectory());\n console.log(path);\n }\n """')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n script"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("load"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n session"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("detach"),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("JS")]),t._v(" 脚本可以保存到本地文件中再进行读取:")]),t._v(" "),a("div",{staticClass:"language-python extra-class"},[a("pre",{pre:!0,attrs:{class:"language-python"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" codecs"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("open")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'./xxx.js'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'r'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'utf-8'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),t._v(" f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n source "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("read"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nscript "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" session"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("create_script"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("source"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("h4",{attrs:{id:"_1-6、python-与-js-交互"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_1-6、python-与-js-交互"}},[t._v("#")]),t._v(" 1.6、"),a("code",[t._v("Python")]),t._v(" 与 "),a("code",[t._v("JS")]),t._v(" 交互")]),t._v(" "),a("p",[t._v("向 "),a("code",[t._v("JS")]),t._v(" 端传递参数,"),a("code",[t._v("JS")]),t._v(" 端处理完成后将结果返回给 "),a("code",[t._v("Python")]),t._v(" 端,这种场景还是很常见的,那应该怎么做呢?")]),t._v(" "),a("p",[t._v("示例代码如下:")]),t._v(" "),a("div",{staticClass:"language-python extra-class"},[a("pre",{pre:!0,attrs:{class:"language-python"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" frida\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" threading\n\ng_event "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" threading"),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 punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 同步")]),t._v("\n\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("payload_message")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("payload"),a("span",{pre:!0,attrs:{class:"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('# print("payload_message -- ", payload)')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"msg"')]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" payload"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("payload"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"msg"')]),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 string"}},[t._v("'status'")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" payload"),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(" payload"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'status'")]),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("'success'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n g_event"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("set")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),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("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("on_message")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("message"),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('# print("on_message message -- ", message)')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" message"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'type'")]),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("'send'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n payload_message"),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 string"}},[t._v("'payload'")]),a("span",{pre:!0,attrs:{class:"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("elif")]),t._v(" message"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'type'")]),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("'error'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),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 string"}},[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\n\nSCRIPT_JS "),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 triple-quoted-string string"}},[t._v("\"\"\"\n function handleMessage(message) {\n var cmd = message['cmd'] \n if (cmd == 'GetDirectory') {\n var name = message['name']\n var path;\n switch (name) {\n case 'home':\n var NSHomeDirectory = new NativeFunction(ptr(Module.findExportByName(\"Foundation\", \"NSHomeDirectory\")), 'pointer', []);\n path = new ObjC.Object(NSHomeDirectory());\n break;\n case 'tmp':\n var NSTemporaryDirectory = new NativeFunction(ptr(Module.findExportByName(\"Foundation\", \"NSTemporaryDirectory\")), 'pointer', []);\n path = new ObjC.Object(NSTemporaryDirectory());\n break;\n default:\n path = \"写的啥呀\"\n }\n if (path) send({msg: path.toString()});\n } \n send({status: 'success'});\n }\n recv(handleMessage);\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("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getDirectory")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("target_process"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n device "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" frida"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get_usb_device"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n session "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" device"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("attach"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("target_process"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n script "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" session"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("create_script"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("SCRIPT_JS"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n script"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("on"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'message'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" on_message"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n script"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("load"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n script"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("post"),a("span",{pre:!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("'cmd'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'GetDirectory'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'name'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n g_event"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),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("\n session"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("detach"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),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("if")]),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("'__main__'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# getDirectory('Twitter', 'home')")]),t._v("\n getDirectory"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Twitter'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'tmp'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("ol",[a("li",[a("code",[t._v("g_event")]),t._v(" 是为了保证同步")]),t._v(" "),a("li",[a("code",[t._v("JS")]),t._v(" 端可以设置 "),a("code",[t._v("recv()")]),t._v(" 的回调接收 "),a("code",[t._v("Python")]),t._v(" 端的消息")]),t._v(" "),a("li",[a("code",[t._v("Python")]),t._v(" 端通过 "),a("code",[t._v("script.on()")]),t._v(" 设置回调,再使用 "),a("code",[t._v("script.post()")]),t._v(" 将参数传递给 "),a("code",[t._v("JS")]),t._v(" 端,然后调用 "),a("code",[t._v("g_event.wait()")]),t._v(" 进入等待状态")]),t._v(" "),a("li",[a("code",[t._v("JS")]),t._v(" 端内部处理完成后,使用 "),a("code",[t._v("send()")]),t._v(" 将 "),a("code",[t._v("{status: 'success'}")]),t._v(" 传递给 "),a("code",[t._v("Python")]),t._v(" 端")]),t._v(" "),a("li",[t._v("在 "),a("code",[t._v("on_message")]),t._v(" 回调中取到 "),a("code",[t._v("JS")]),t._v(" 端返回的数据,当识别到 "),a("code",[t._v("status")]),t._v(" 为 "),a("code",[t._v("success")]),t._v(" 后,调用 "),a("code",[t._v("g_event.set()")]),t._v(" 使主线程继续执行")])]),t._v(" "),a("h3",{attrs:{id:"_2、拦截某个类的所有方法"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_2、拦截某个类的所有方法"}},[t._v("#")]),t._v(" 2、拦截某个类的所有方法")]),t._v(" "),a("p",[t._v("如果想对某个类的所有方法进行批量拦截,可以使用 "),a("code",[t._v("ApiResolver")]),t._v(" 接口,它可以根据正则表达式获取符合条件的所有方法")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" resolver "),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("ApiResolver")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'objc'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nresolver"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("enumerateMatches")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'*[T1TranslateButton *]'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[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-variable function"}},[t._v("onMatch")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("match")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n console"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("log")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("match"),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("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),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(" match"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'address'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"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-variable function"}},[t._v("onComplete")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),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("}")]),t._v("\n"),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-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("+"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton tfn_defaultShouldFlipForRightToLeftTransform"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be4014\n+"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton button"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be2774\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton tapActionBlock"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be4280\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton setTapActionBlock:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be4290\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton translationSource"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be4250\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton setLogoTapActionBlock:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be42ac\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton setShowingTranslation:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be2c10\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton setOriginalLanguage:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be2b58\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton setTranslationSource:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be2cec\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton _didTap:forEvent:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be296c\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton _t1_didHover:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be4140\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton setSelectionPadding:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be42d8\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton touchRect"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be42f8\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton _t1_isTouchingLogo:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be2a8c\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton touchLogoRect"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be4328\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton _t1_buttonTitle"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be2f28\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton autoTranslationExpanded"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be4270\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton _t1_imageHeightOffsetForLogo:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be3164\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton _t1_titleRectWithTitleString:origin:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be32b8\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton _t1_imageRectWithOrigin:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be3394\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton _t1_drawRectFor:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be3f94\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton setTouchRect:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be4310\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton _t1_drawHighlightWithContext:andRect:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be401c\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton setTouchLogoRect:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be4340\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton setAutoTranslationExpanded:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be2cc4\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton originalLanguage"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be4240\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton showingTranslation"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be4260\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton logoTapActionBlock"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be429c\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton selectionPadding"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be42c8\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton _dynamicColorsDidReload:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be3ed0\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton titleText"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be42b8\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton _titleColor"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be2ea8\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton logoImage"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be42e8\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton _logoImage"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be2d14\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton dealloc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be28e4\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton .cxx_destruct"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be4358\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton initWithFrame:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be27dc\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton sizeThatFits:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be3408\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton setHighlighted:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be2b08\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton drawRect:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be3798\n-"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1TranslateButton setTitleText:"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(":0x101be2c38\n")])])]),a("h3",{attrs:{id:"_3、替换原方法"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_3、替换原方法"}},[t._v("#")]),t._v(" 3、替换原方法")]),t._v(" "),a("p",[a("code",[t._v("Interceptor.attach()")]),t._v(" 可以在拦截目标后,打印参数,修改返回值,但无法阻止原方法的执行")]),t._v(" "),a("p",[t._v("我们可以给原方法的 "),a("code",[t._v("implementation")]),t._v(" 进行赋值,从而覆盖其实现")]),t._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[t._v("var didTap = ObjC.classes.T1TranslateButton['- _didTap:forEvent:']\n\nvar didTapOldImp = didTap.implementation\n\n// 覆盖实现\ndidTap.implementation = ObjC.implement(setTitle, function(handle, selector, arg1, arg2) {\n\n var self = ObjC.Object(handle)\n console.log(\"self -- \", self) \n\n // 调用旧实现\n // didTapOldImp(handle, selector, arg1, arg2)\n})\n")])])]),a("p",[t._v("这里需要注意的是,像 "),a("code",[t._v("_didTap:forEvent:")]),t._v(" 这里需要传递两个参数,则 "),a("code",[t._v("ObjC.implement")]),t._v(" 的回调中也需要写明两个参数("),a("code",[t._v("arg1")]),t._v("、"),a("code",[t._v("arg2")]),t._v("),即需要多少参数就写多少,没有则不用写")]),t._v(" "),a("h3",{attrs:{id:"_4、rpc-调用"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_4、rpc-调用"}},[t._v("#")]),t._v(" 4、"),a("code",[t._v("RPC")]),t._v(" 调用")]),t._v(" "),a("p",[a("code",[t._v("RPC")]),t._v(":即 "),a("code",[t._v("Remote Procedure Call")]),t._v(",远程过程调用,开发人员可以将封装好的任意函数指定为 "),a("code",[t._v("RPC")]),t._v(" 函数,以提供给 "),a("code",[t._v("Python")]),t._v(" 使用。")]),t._v(" "),a("p",[t._v("利用 "),a("code",[t._v("rpc.exports = {}")]),t._v(" 导出 "),a("code",[t._v("RPC")]),t._v(" 函数,多个函数以逗号分隔,注意:方法名需要全小写!")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getHomeDirectory")]),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(" NSHomeDirectory "),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("NativeFunction")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("ptr")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Module"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("findExportByName")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Foundation"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"NSHomeDirectory"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[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 string"}},[t._v("'pointer'")]),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(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" path "),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("ObjC"),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 function"}},[t._v("NSHomeDirectory")]),a("span",{pre:!0,attrs:{class:"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("return")]),t._v(" path"),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("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("openUrl")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("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 "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" UIApplication "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ObjC"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("classes"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("UIApplication"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("sharedApplication")]),a("span",{pre:!0,attrs:{class:"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(" toOpen "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ObjC"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("classes"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token constant"}},[t._v("NSURL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("URLWithString_")]),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 "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" UIApplication"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("openURL_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("toOpen"),a("span",{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("function")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("playSystemSound")]),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(" playSound "),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("NativeFunction")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Module"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("findExportByName")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'AudioToolbox'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'AudioServicesPlaySystemSound'")]),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("'void'")]),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("'int'")]),a("span",{pre:!0,attrs:{class:"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("playSound")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1111")]),a("span",{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("// 导出 RPC 函数")]),t._v("\nrpc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("exports "),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 function-variable function"}},[t._v("openurl")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("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 "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("openUrl")]),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 "),a("span",{pre:!0,attrs:{class:"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-variable function"}},[t._v("sound")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),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("playSystemSound")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"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-variable function"}},[t._v("alert")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),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("showAlert")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"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-variable function"}},[t._v("homedirectory")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),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(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// homedirectory 必须小写")]),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("getHomeDirectory")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!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("Python")]),t._v(" 端使用 "),a("code",[t._v("rpc.js")])]),t._v(" "),a("div",{staticClass:"language-python extra-class"},[a("pre",{pre:!0,attrs:{class:"language-python"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" codecs\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" frida\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),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("'__main__'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n device "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" frida"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get_usb_device"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n session "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" device"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("attach"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Twitter'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 读取 JS 脚本 ")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" codecs"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("open")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'./rpc.js'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'r'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'utf-8'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),t._v(" f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n source "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("read"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n script "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" session"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("create_script"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("source"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n script"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("load"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n rpc "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" script"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("exports\n rpc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("openurl"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"https://fullstackaction.com"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n rpc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sound"),a("span",{pre:!0,attrs:{class:"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("print")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("rpc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("homeDirectory"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"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("# print(rpc)")]),t._v("\n\n session"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("detach"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("h2",{attrs:{id:"六、最后"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#六、最后"}},[t._v("#")]),t._v(" 六、最后")]),t._v(" "),a("p",[t._v("以上代码已经上传至:"),a("a",{attrs:{href:"https://github.com/LinXunFeng/frida_study",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/LinXunFeng/frida_study"),a("OutboundLink")],1)]),t._v(" "),a("p",[a("code",[t._v("Frida")]),t._v(" 提供的 "),a("code",[t._v("API")]),t._v(" 接口十分丰富,这里只提到了常用的内容,更多内容还是需要我们一起去阅读官方文档:"),a("a",{attrs:{href:"https://frida.re/docs/javascript-api",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://frida.re/docs/javascript-api"),a("OutboundLink")],1)]),t._v(" "),a("p",[t._v("除此之外,"),a("a",{attrs:{href:"https://codeshare.frida.re/",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://codeshare.frida.re"),a("OutboundLink")],1),t._v(" 上提供很多共享脚本,大家可以用来学习和引入使用")])])}),[],!1,null,null,null);a.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/115.f9a34f03.js b/assets/js/115.f9a34f03.js new file mode 100644 index 000000000..4f9f4a424 --- /dev/null +++ b/assets/js/115.f9a34f03.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[115],{428:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h2",{attrs:{id:"一、抽象类的使用"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#一、抽象类的使用"}},[t._v("#")]),t._v(" 一、抽象类的使用")]),t._v(" "),s("p",[s("code",[t._v("Dart")]),t._v(" 抽象类可以只声明方法,也可以有具体的方法实现,但是不能直接用抽象类来创建实例,只能被继承使用或者充当接口。")]),t._v(" "),s("p",[t._v("定义一个抽象类 "),s("code",[t._v("Animal")])]),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("Animal")]),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("// 仅声明eat方法")]),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("eat")]),s("span",{pre:!0,attrs:{class:"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("// 声明方法,且有具体实现")]),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("sleep")]),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("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[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 "),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 keyword"}},[t._v("class")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Cat")]),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("Animal")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token metadata function"}},[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("eat")]),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("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[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 "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("sleep")]),s("span",{pre:!0,attrs:{class:"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 comment"}},[t._v("// 可以不实现 sleep 方法")]),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",[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("Cat")]),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("Animal")]),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("void")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("eat")]),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("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[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 "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 必须实现 sleep 方法")]),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("sleep")]),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("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[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 "),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 keyword"}},[t._v("final")]),t._v(" animal "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Animal")]),s("span",{pre:!0,attrs:{class:"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 comment"}},[t._v("// Error: The class 'Test' is abstract and can't be instantiated.")]),t._v("\n")])])]),s("blockquote",[s("ul",[s("li",[t._v("抽象类不能实例化。")]),t._v(" "),s("li",[t._v("继承: 子类比较实现抽象方法,子类可以不重写抽象类中已实现的方法。")]),t._v(" "),s("li",[t._v("接口: 必须实现抽象类中声明的所有方法")])])]),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("Dart")]),t._v(" 提供的 "),s("code",[t._v("Map")]),t._v(" 和 "),s("code",[t._v("List")]),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("final")]),t._v(" list "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"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(" dict "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Map")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("dynamic")]),s("span",{pre:!0,attrs:{class:"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("p",[t._v("我们来看一下 "),s("code",[t._v("Map")]),t._v(" 的源码:")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110220715.png",alt:"Map源码"}})]),t._v(" "),s("p",[s("code",[t._v("Map")]),t._v(" 的确是抽象类,不过此时我们也注意到了,在 "),s("code",[t._v("Map")]),t._v(" 这个抽象类中,定义了一个工厂构造方法,"),s("strong",[t._v("这就是使抽象类可实例化的关键所在,因为工厂方法可以返回一个实例对象,但这个对象的类型不一定就是当前类!")])]),t._v(" "),s("p",[t._v("在这个地方,"),s("code",[t._v("Map")]),t._v(" 的工厂方法并没有具体的实现,而只是在工厂构造方法前加了一个关键字 "),s("code",[t._v("external")]),t._v("。\n"),s("code",[t._v("external")]),t._v(" 关键字可以让方法的声明与实现分离,即 可以由外部来帮我们完成具体的方法实现,那外部如何才能关联到该声明的方法呢?这里就需要用到注解 "),s("code",[t._v("@patch")]),t._v(",使外部的方法实现与该声明的方法绑定")]),t._v(" "),s("blockquote",[s("p",[s("code",[t._v("external")]),t._v(" 可以分离方法的声明与实现\n"),s("code",[t._v("@patch")]),t._v(" 关联某个类中用 "),s("code",[t._v("external")]),t._v(" 修饰的方法的实现")])]),t._v(" "),s("p",[t._v("根据如下路径可以找到 "),s("code",[t._v("Map")]),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("// flutter/bin/cache/dart-sdk/lib/_internal/vm/lib/map_patch.dart")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@patch")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("factory")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[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(" "),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("LinkedHashMap")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("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("p",[t._v("可以看到,这里使用了 "),s("code",[t._v("LinkedHashMap")]),t._v(" 来实现 "),s("code",[t._v("Map")]),t._v(" 。")]),t._v(" "),s("p",[t._v("我们再去看一下 "),s("code",[t._v("LinkedHashMap")]),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("// flutter/bin/cache/dart-sdk/lib/collection/linked_hash_map.dart")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("external")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("factory")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LinkedHashMap")]),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("bool "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Function")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" equals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n int "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Function")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" hashCode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n bool "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Function")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("dynamic")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" isValidKey"),s("span",{pre:!0,attrs:{class:"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("LinkedHashMap")]),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("// flutter/bin/cache/dart-sdk/lib/_internal/vm/lib/collection_patch.dart")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@patch")]),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("LinkedHashMap")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("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 function"}},[t._v("@patch")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("factory")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LinkedHashMap")]),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("bool "),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 class-name"}},[t._v("K")]),t._v(" key1"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),t._v(" key2"),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 punctuation"}},[t._v(",")]),t._v("\n int "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("hashCode")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),t._v(" key"),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 punctuation"}},[t._v(",")]),t._v("\n bool "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("isValidKey")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("potentialKey"),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 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("isValidKey "),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 "),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("hashCode "),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 "),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("equals "),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 "),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(" _InternalLinkedHashMap"),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("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 punctuation"}},[t._v("}")]),t._v("\n hashCode "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" _defaultHashCode"),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("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("identical")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("identityHashCode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" hashCode"),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 "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("identical")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("identical"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" equals"),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 keyword"}},[t._v("new")]),t._v(" _CompactLinkedIdentityHashMap"),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("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 punctuation"}},[t._v("}")]),t._v("\n equals "),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 operator"}},[t._v("=")]),t._v(" _defaultEquals"),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 hashCode "),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 operator"}},[t._v("=")]),t._v(" _defaultHashCode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n equals "),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 operator"}},[t._v("=")]),t._v(" _defaultEquals"),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(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" _CompactLinkedCustomHashMap"),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("V")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("equals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" hashCode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" isValidKey"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 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("LinkedHashMap")]),t._v("的工厂构造方法返回的实例类型是 "),s("code",[t._v("_InternalLinkedHashMap")]),t._v(" 或 "),s("code",[t._v("_CompactLinkedCustomHashMap")]),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("// flutter/bin/cache/dart-sdk/lib/_internal/vm/lib/compact_hash.dart")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@pragma")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"vm:entry-point"')])]),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(" _InternalLinkedHashMap"),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("V")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" _HashVMBase\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MapMixin")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("V")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n _LinkedHashMapMixin"),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("V")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n _HashBase"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n _OperatorEqualsAndHashCode\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("implements")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LinkedHashMap")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("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("_InternalLinkedHashMap")]),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 _index "),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("Uint32List")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("_HashBase"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("_INITIAL_INDEX_SIZE"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n _hashMask "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" _HashBase"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_indexSizeToHashMask")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("_HashBase"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("_INITIAL_INDEX_SIZE"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n _data "),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("List")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("filled")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("_HashBase"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("_INITIAL_INDEX_SIZE"),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 _usedData "),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 _deletedKeys "),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 "),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 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(".")]),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(" _CompactLinkedIdentityHashMap"),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("V")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" _HashFieldBase\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MapMixin")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("V")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n _LinkedHashMapMixin"),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("V")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n _HashBase"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n _IdenticalAndIdentityHashCode\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("implements")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LinkedHashMap")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("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("_CompactLinkedIdentityHashMap")]),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("_HashBase"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("_INITIAL_INDEX_SIZE"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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(" _CompactLinkedCustomHashMap"),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("V")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" _HashFieldBase\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MapMixin")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("V")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" _LinkedHashMapMixin"),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("V")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" _HashBase\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("implements")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LinkedHashMap")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("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("final")]),t._v(" _equality"),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(" _hasher"),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(" _validKey"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// TODO(koda): Ask gbracha why I cannot have fields _equals/_hashCode.")]),t._v("\n int "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_hashCode")]),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 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("_hasher")]),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 bool "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_equals")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e1"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" e2"),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("_equality")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e1"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" e2"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n bool "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("containsKey")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Object")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" o"),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("_validKey")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("o"),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("super")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("containsKey")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("o"),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 boolean"}},[t._v("false")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("V")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("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("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Object")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" o"),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("_validKey")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("o"),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("super")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("o"),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("null")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("V")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("remove")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Object")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" o"),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("_validKey")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("o"),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("super")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("remove")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("o"),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("null")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_CompactLinkedCustomHashMap")]),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("_equality"),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("_hasher"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" validKey"),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(" _validKey "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("validKey "),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 operator"}},[t._v("?")]),t._v(" validKey "),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(" _TypeTest"),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("K")]),s("span",{pre:!0,attrs:{class:"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("test"),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("(")]),t._v("_HashBase"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("_INITIAL_INDEX_SIZE"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("Map")]),t._v(" 中的 "),s("code",[t._v("external factory Map();")]),t._v(" 最终返回的最终实例类型为 "),s("code",[t._v("_InternalLinkedHashMap")]),t._v(" 或 "),s("code",[t._v("_CompactLinkedCustomHashMap")])]),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("final")]),t._v(" map "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Map")]),s("span",{pre:!0,attrs:{class:"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("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("map"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("runtimeType"),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("// 打印结果")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// _InternalLinkedHashMap")]),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 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("Animal")]),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("void")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("eat")]),s("span",{pre:!0,attrs:{class:"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("void")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("sleep")]),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("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[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 "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("factory")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Animal")]),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 class-name"}},[t._v("Cat")]),s("span",{pre:!0,attrs:{class:"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 keyword"}},[t._v("class")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Cat")]),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("Animal")]),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("void")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("eat")]),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("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[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 "),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("sleep")]),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("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[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 "),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("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("final")]),t._v(" animal "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Animal")]),s("span",{pre:!0,attrs:{class:"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("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("animal"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("runtimeType"),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("// 打印结果: Cat")]),t._v("\n")])])]),s("p",[t._v("可能会有同学要问了,这里用的是接口的方式,可以用继承的方式吗?\n"),s("strong",[t._v("很遗憾不行,因为在抽象类中定义了工厂构造方法后,在子类中不能定义除工厂构造方法外的其它构造方法了,会报错~")])]),t._v(" "),s("p",[t._v("总结一下:")]),t._v(" "),s("blockquote",[s("p",[t._v("抽象类无法直接创建实例,但是可以通过实现工厂构造方法来间接实现抽象类的实例化!")])]),t._v(" "),s("h2",{attrs:{id:"三、补充"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#三、补充"}},[t._v("#")]),t._v(" 三、补充")]),t._v(" "),s("p",[t._v("那饶了这么一大圈,为什么不直接在声明的时候就给它实现了呢?🤔\n这样做的好处就是:")]),t._v(" "),s("blockquote",[s("ul",[s("li",[t._v("复用同一套API的声明")]),t._v(" "),s("li",[t._v("可以针对不同的平台做不同的实现")])])]),t._v(" "),s("p",[t._v("而 "),s("code",[t._v("针对不同的平台做不同的实现")]),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("// flutter/bin/cache/dart-sdk/lib/io/file_system_entity.dart")]),t._v("\n"),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(" _FileSystemWatcher "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("external")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("static")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Stream")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FileSystemEvent")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_watch")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" int events"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" bool recursive"),s("span",{pre:!0,attrs:{class:"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("external")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("static")]),t._v(" bool "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("get")]),t._v(" isSupported"),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("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("// flutter/bin/cache/dart-sdk/lib/_internal/vm/bin/file_patch.dart")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@patch")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("static")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Stream")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FileSystemEvent")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_watch")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" int events"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" bool recursive"),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("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Platform")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isLinux"),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 function"}},[t._v("_InotifyFileSystemWatcher")]),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(" events"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" recursive"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("_stream"),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("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Platform")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isWindows"),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 function"}},[t._v("_Win32FileSystemWatcher")]),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(" events"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" recursive"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("_stream"),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("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Platform")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isMacOS"),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 function"}},[t._v("_FSEventStreamFileSystemWatcher")]),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(" events"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" recursive"),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("_stream"),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("throw")]),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("FileSystemException")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"File system watching is not supported on this platform"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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")])])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/116.e3223d76.js b/assets/js/116.e3223d76.js new file mode 100644 index 000000000..477e142ca --- /dev/null +++ b/assets/js/116.e3223d76.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[116],{429:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[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("iOS")]),t._v(" 开发时这个功能很常用, 在 "),s("code",[t._v("OC")]),t._v(" 和 "),s("code",[t._v("Swift")]),t._v(" 中都可以很轻松实现,因为系统本来就提供了用于日志输出的预处理宏,只要我们拿来拼接就可以了,但是在 "),s("code",[t._v("Dart")]),t._v(" 中并不提供这些,那有什么办法实现它呢?")]),t._v(" "),s("p",[t._v("我们回想在开发过程中,是不是发现只要一不小心抛异常,就可以看到类似如下的打印内容,而且还能清楚的知道异常是在哪个文件和哪一行的代码造成的。")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110220751.png",alt:""}})]),t._v(" "),s("blockquote",[s("p",[t._v("所以如果我们可以在调用函数时拿到当前调用堆栈,就可以取到一系列想要的数据。")])]),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("dart:core")]),t._v(" 中提供了 "),s("code",[t._v("堆栈跟踪(StackTrace)")]),t._v(",可以通过 "),s("code",[t._v("StackTrace.current")]),t._v(" 取到当前的堆栈信息,打印如下图所示,会发现这不好拿到我们想要的信息。")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110220807.png",alt:""}})]),t._v(" "),s("p",[t._v("这里我用到了官方开发的一个包 "),s("a",{attrs:{href:"https://pub.dev/packages/stack_trace",target:"_blank",rel:"noopener noreferrer"}},[t._v("stack_trace"),s("OutboundLink")],1),t._v(",它可以将堆栈信息变得更多人性化,并方便我们查看堆栈信息和获取想要的数据。")]),t._v(" "),s("p",[s("strong",[t._v("ps: "),s("code",[t._v("stack_trace")]),t._v(" 在 "),s("code",[t._v("Flutter")]),t._v(" 环境下直接导包即可使用,而在纯 "),s("code",[t._v("Dart")]),t._v(" 下需要将其添加为依赖于"),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("dependencies")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("stack_trace")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" ^1.9.3\n")])])]),s("p",[t._v("那下面我们来试试 "),s("a",{attrs:{href:"https://pub.dev/packages/stack_trace",target:"_blank",rel:"noopener noreferrer"}},[t._v("stack_trace"),s("OutboundLink")],1),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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:stack_trace/stack_trace.dart'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 将 StackTrace 对象转换成 Chain 对象")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 当然,这里也可以直接用 Chain.current();")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" chain "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Chain")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("forTrace")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("StackTrace")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("current"),s("span",{pre:!0,attrs:{class:"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("final")]),t._v(" frames "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" chain"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("toTrace")]),s("span",{pre:!0,attrs:{class:"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("frames"),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(" frame "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" frames"),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(";")]),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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"所在文件:')]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("${")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("frame"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("uri")]),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 interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("${")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("frame"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("line")]),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 interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("${")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("frame"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("column")]),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\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 打印结果")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// flutter: 所在文件:package:flutterlog/main.dart 所在行 55 所在列 23")]),t._v("\n")])])]),s("h2",{attrs:{id:"三、呈上代码"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#三、呈上代码"}},[t._v("#")]),t._v(" 三、呈上代码")]),t._v(" "),s("p",[t._v("下面我做了一点封装,直接拿走即可使用,打印效果如下所示:")]),t._v(" "),s("p",[t._v("完整的代码和示例请到GitHub上"),s("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_log",target:"_blank",rel:"noopener noreferrer"}},[t._v("【查看】"),s("OutboundLink")],1),t._v("。")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110220824.png",alt:""}})]),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 comment"}},[t._v("// log.dart")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FLogMode")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n debug"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 💚 DEBUG")]),t._v("\n warning"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 💛 WARNING")]),t._v("\n info"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 💙 INFO")]),t._v("\n error"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ❤️ ERROR")]),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 class-name"}},[t._v("FLog")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("dynamic")]),t._v(" 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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FLogMode")]),t._v(" mode "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FLogMode")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("debug "),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("kReleaseMode"),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("// release模式不打印")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),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("var")]),t._v(" chain "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Chain")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("current")]),s("span",{pre:!0,attrs:{class:"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("// Chain.forTrace(StackTrace.current);")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 将 core 和 flutter 包的堆栈合起来(即相关数据只剩其中一条)")]),t._v("\n chain "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" chain"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("foldFrames")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("frame"),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(" frame"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isCore "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("||")]),t._v(" frame"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("package "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"flutter"')])]),s("span",{pre:!0,attrs:{class:"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("final")]),t._v(" frames "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" chain"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("toTrace")]),s("span",{pre:!0,attrs:{class:"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("frames"),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("final")]),t._v(" idx "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" frames"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("indexWhere")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("element"),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(" element"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("member "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"FLog"')])]),s("span",{pre:!0,attrs:{class:"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("idx "),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")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("||")]),t._v(" idx"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">=")]),t._v(" frames"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("length"),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")]),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 keyword"}},[t._v("final")]),t._v(" frame "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" frames"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("idx"),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\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" modeStr "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[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 keyword"}},[t._v("switch")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("mode"),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("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FLogMode")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("debug"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n modeStr "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"💚 DEBUG"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FLogMode")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("warning"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n modeStr "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"💛 WARNING"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FLogMode")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("info"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n modeStr "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"💙 INFO"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FLogMode")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("error"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n modeStr "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"❤️ ERROR"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),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("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"')]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("$")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("modeStr")])]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v(' ${frame.uri.toString().split("')])]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('").last}(')]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("${")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("frame"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("line")]),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 interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("$")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("msg")])]),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"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/117.8634c1d7.js b/assets/js/117.8634c1d7.js new file mode 100644 index 000000000..a8839ba83 --- /dev/null +++ b/assets/js/117.8634c1d7.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[117],{557:function(t,a,s){"use strict";s.r(a);var n=s(8),e=Object(n.a)({},(function(){var t=this,a=t._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("blockquote",[a("p",[t._v("目前大多数公司都有自己开发多年的项目,不可能直接用 "),a("code",[t._v("Flutter")]),t._v(" 从头开发一套,那样不实现,除非是小项目,因此只能是在原有的基础上用 "),a("code",[t._v("Flutter")]),t._v(" 来开发新业务或重构旧业务,而这里就需要用到 "),a("code",[t._v("Flutter")]),t._v(" 的 "),a("code",[t._v("混合开发")])])]),t._v(" "),a("h2",{attrs:{id:"一、创建flutter模块"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#一、创建flutter模块"}},[t._v("#")]),t._v(" 一、创建Flutter模块")]),t._v(" "),a("p",[t._v("使用混合开发就不能像之前一样直接上来就创建一个 "),a("code",[t._v("Flutter")]),t._v(" 项目,而是要使用 "),a("code",[t._v("Flutter模板")])]),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 comment"}},[t._v("# flutter_module_lxf 可以随便你命名")]),t._v("\nflutter create "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--template")]),t._v(" module flutter_module_lxf\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# --template 可以替换为 -t")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# flutter create -t module flutter_module_lxf")]),t._v("\n")])])]),a("p",[t._v("创建出来的 "),a("code",[t._v("Flutter")]),t._v(" 模块依然是可以像之前创建的"),a("code",[t._v("Flutter项目")]),t._v(" 一样打开和运行的。")]),t._v(" "),a("p",[t._v("目录下有也有 "),a("code",[t._v("ios")]),t._v(" 和 "),a("code",[t._v("android")]),t._v(" 目录,只不过前面加了个点 ,成了点目录。")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110220845.png",alt:""}})]),t._v(" "),a("h2",{attrs:{id:"二、ios"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#二、ios"}},[t._v("#")]),t._v(" 二、iOS")]),t._v(" "),a("h3",{attrs:{id:"集成"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#集成"}},[t._v("#")]),t._v(" 集成")]),t._v(" "),a("blockquote",[a("p",[t._v("通过 "),a("code",[t._v("Cocoapods")]),t._v(" ,将 "),a("code",[t._v("Flutter")]),t._v(" 模块编译成一个库,再到原生项目中进行引入和使用即可")])]),t._v(" "),a("p",[t._v("在 "),a("code",[t._v("Podfile")]),t._v(" 中添加两行配置")]),t._v(" "),a("div",{staticClass:"language-ruby extra-class"},[a("pre",{pre:!0,attrs:{class:"language-ruby"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 指定我们刚刚创建的 Flutter 模块的路径")]),t._v("\nflutter_application_path "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'../flutter_module_lxf'")])]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 拼接脚本文件的路径: .ios/Flutter/podhelper.rb")]),t._v("\nload "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("File")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("join"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("flutter_application_path"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'.ios'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Flutter'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'podhelper.rb'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("在每个需要引用 "),a("code",[t._v("Flutter")]),t._v(" 的 "),a("code",[t._v("Target")]),t._v(" 下,都需要添加一行配置")]),t._v(" "),a("div",{staticClass:"language-ruby extra-class"},[a("pre",{pre:!0,attrs:{class:"language-ruby"}},[a("code",[t._v("install_all_flutter_pods"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("flutter_application_path"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("添加后如下所示:")]),t._v(" "),a("div",{staticClass:"language-ruby extra-class"},[a("pre",{pre:!0,attrs:{class:"language-ruby"}},[a("code",[t._v("flutter_application_path "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'../flutter_module_lxf'")])]),t._v("\nload "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("File")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("join"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("flutter_application_path"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'.ios'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Flutter'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'podhelper.rb'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\nuse_frameworks"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("\ntarget "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'LXFFlutterHybridDemo'")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("do")]),t._v("\n \n install_all_flutter_pods"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("flutter_application_path"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n \n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("end")]),t._v("\n")])])]),a("p",[t._v("添加完成后,执行一次 "),a("code",[t._v("pod install")])]),t._v(" "),a("p",[t._v("混合开发混合开发"),a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110220859.png",alt:""}})]),t._v(" "),a("h3",{attrs:{id:"使用"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#使用"}},[t._v("#")]),t._v(" 使用")]),t._v(" "),a("blockquote",[a("p",[t._v("两个步骤")]),t._v(" "),a("ol",[a("li",[t._v("获取 Flutter引擎 "),a("code",[t._v("FlutterEngine")])]),t._v(" "),a("li",[t._v("通过 "),a("code",[t._v("FlutterEngine")]),t._v(" 创建 "),a("code",[t._v("FlutterViewController")])])])]),t._v(" "),a("h4",{attrs:{id:"基本使用"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#基本使用"}},[t._v("#")]),t._v(" 基本使用")]),t._v(" "),a("p",[a("code",[t._v("AppDelegate")]),t._v(" 类中声明一个 "),a("code",[t._v("FlutterEngine")]),t._v(" 变量,在 "),a("code",[t._v("didFinishLaunchingWithOptions")]),t._v(" 方法中启动 "),a("code",[t._v("Flutter引擎")])]),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 comment"}},[t._v("// AppDelegate.swift")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Flutter")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token attribute atrule"}},[t._v("@UIApplicationMain")]),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("AppDelegate")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIResponder")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIApplicationDelegate")]),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("// 创建 Flutter引擎")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("lazy")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" flutterEngine "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterEngine")]),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-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"lxf"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("application")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token omit keyword"}},[t._v("_")]),t._v(" application"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIApplication")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" 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 class-name"}},[t._v("UIApplication")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LaunchOptionsKey")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[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("->")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Bool")]),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("// 启动 Flutter引擎")]),t._v("\n flutterEngine"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("run")]),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 boolean"}},[t._v("true")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n \n "),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("\n")])])]),a("p",[a("code",[t._v("ViewController")]),t._v(" 中添加一个按钮,点击弹出 "),a("code",[t._v("Flutter模块")])]),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 comment"}},[t._v("// ViewController.swift")]),t._v("\n\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-definition function"}},[t._v("viewDidLoad")]),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("viewDidLoad")]),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(" btn "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIButton")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("type"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("custom"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n btn"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("frame "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CGRect")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[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(" y"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("200")]),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")]),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("44")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n btn"),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 punctuation"}},[t._v(".")]),t._v("black\n btn"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("addTarget")]),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(" action"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token other-directive property"}},[t._v("#selector")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("showFlutterVc"),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("for")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("touchUpInside"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n btn"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setTitle")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"弹出Flutter模块"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),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 keyword"}},[t._v("self")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("view"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("addSubview")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("btn"),a("span",{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 attribute atrule"}},[t._v("@objc")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("showFlutterVc")]),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("// 创建FlutterViewController")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里的 engine 可以传 nil,Flutter会帮我们自动创建一个引擎,但是性能较差")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" flutterVc "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterViewController")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("engine"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("fetchFlutterEngine")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" nibName"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" bundle"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),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 function"}},[t._v("present")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("flutterVc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" animated"),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(" completion"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),a("span",{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("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("fetchFlutterEngine")]),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 class-name"}},[t._v("FlutterEngine")]),t._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 class-name"}},[t._v("UIApplication")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("shared"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("delegate "),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 class-name"}},[t._v("AppDelegate")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("flutterEngine\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110220934.jpg",alt:""}})]),t._v(" "),a("p",[t._v("如果遇到报 "),a("code",[t._v("Command PhaseScriptExecution failed with a nonzero exit code")]),t._v(" 错误,如下图所示:")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110220918.png",alt:""}})]),t._v(" "),a("p",[t._v("请先用 "),a("code",[t._v("Android Studio")]),t._v(" 或 "),a("code",[t._v("VSCode")]),t._v(" 打开 "),a("code",[t._v("Flutter模块")]),t._v(" 项目并运行到iOS设备上,让其帮我们对iOS项目进行一些初始化配置。成功运行后就可以关闭 "),a("code",[t._v("Flutter模块")]),t._v(" 项目的运行了,接着再用 "),a("code",[t._v("Xcode")]),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("Flutter引擎")]),t._v(" 在 "),a("code",[t._v("run")]),t._v(" 之前,通过 "),a("code",[t._v("invokeMethod")]),t._v(" 调用 "),a("code",[t._v("setInitialRoute")]),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 comment"}},[t._v("// 修改初始路由")]),t._v("\nflutterEngine"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("navigationChannel"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("invokeMethod")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"setInitialRoute"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" arguments"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/other"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 启动 Flutter引擎")]),t._v("\nflutterEngine"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("run")]),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("Flutter")]),t._v(" 的官方 "),a("code",[t._v("issue")]),t._v(" 上也有人提到这个问题: "),a("a",{attrs:{href:"https://github.com/flutter/flutter/issues/59895",target:"_blank",rel:"noopener noreferrer"}},[t._v("【setInitialRoute is broken for iOS add-to-app #59895】"),a("OutboundLink")],1),t._v(",目前只能官方进行修复和调整 "),a("code",[t._v("API")])]),t._v(" "),a("p",[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("let")]),t._v(" flutterVc "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterViewController")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("project"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterDartProject")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" nibName"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" bundle"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nflutterVc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setInitialRoute")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/other"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),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 function"}},[t._v("present")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("flutterVc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" animated"),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(" completion"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("虽然这么写可以实现这个功能,但是会有明显的类似卡顿的现象,因为使用这种方式去创建 "),a("code",[t._v("FlutterViewController")]),t._v(" 之前,会隐式创建和启动一个 "),a("code",[t._v("FlutterEngine")]),t._v(",而我们弹出 "),a("code",[t._v("FlutterViewController")]),t._v(" 时 "),a("code",[t._v("FlutterEngine")]),t._v(" 还没加载完毕,所以我们会看到先弹出了一个透明的界面,再显示 "),a("code",[t._v("/other")]),t._v(" 路由对应的界面视图。")]),t._v(" "),a("h4",{attrs:{id:"使用-flutterappdelegate"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#使用-flutterappdelegate"}},[t._v("#")]),t._v(" 使用 FlutterAppDelegate")]),t._v(" "),a("p",[t._v("使用 "),a("code",[t._v("FlutterAppDelegate")]),t._v("这个不是必要的操作,但是如果你想让 "),a("code",[t._v("Flutter模块")]),t._v(" 也能使用原生的功能的话,建议使用")]),t._v(" "),a("blockquote",[a("p",[t._v("原生功能")]),t._v(" "),a("ul",[a("li",[t._v("处理 "),a("code",[t._v("openURL")]),t._v(" 的回调")]),t._v(" "),a("li",[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("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 class-name"}},[t._v("FlutterAppDelegate")]),t._v(" \n")])])]),a("p",[t._v("更具体的使用,请阅读 "),a("a",{attrs:{href:"https://flutter.dev/docs/development/add-to-app/ios/add-flutter-screen?tab=no-engine-vc-swift-tab#using-the-flutterappdelegate",target:"_blank",rel:"noopener noreferrer"}},[t._v("官方文档"),a("OutboundLink")],1)]),t._v(" "),a("h2",{attrs:{id:"三、android"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#三、android"}},[t._v("#")]),t._v(" 三、Android")]),t._v(" "),a("p",[t._v("修改安卓项目 根目录下的 "),a("code",[t._v("settings.gradle")]),t._v(" 文件")]),t._v(" "),a("div",{staticClass:"language-groovy extra-class"},[a("pre",{pre:!0,attrs:{class:"language-groovy"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// settings.gradle")]),t._v("\n\ninclude "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("':app'")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// assumed existing content")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setBinding")]),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("Binding")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("gradle"),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("]")]),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("// new")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("evaluate")]),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("(")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// new")]),t._v("\n settingsDir"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("parentFile"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// new")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里的 flutter_module_lxf 请修改为你自己创建的Flutter模板目录名称")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'flutter_module_lxf/.android/include_flutter.groovy'")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// new")]),t._v("\n"),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:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110221002.png",alt:""}})]),t._v(" "),a("p",[t._v("修改安卓项目 "),a("code",[t._v("app")]),t._v(" 目录下的 "),a("code",[t._v("build.gradle")]),t._v(" 文件")]),t._v(" "),a("div",{staticClass:"language-groovy extra-class"},[a("pre",{pre:!0,attrs:{class:"language-groovy"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// app/build.gradle")]),t._v("\n\ndependencies "),a("span",{pre:!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("// 配置flutter依赖")]),t._v("\n implementation "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("project")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("':flutter'")]),a("span",{pre:!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- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[t._v("Default interface methods are only supported starting with Android N (--min-api 24): void androidx.lifecycle.DefaultLifecycleObserver.onCreate(androidx.lifecycle.LifecycleOwner)\n")])])]),a("p",[t._v("请确认是否指定了使用 "),a("code",[t._v("Java 8")]),t._v(" 进行编译 "),a("a",{attrs:{href:"https://flutter.dev/docs/development/add-to-app/android/project-setup#java-8-requirement",target:"_blank",rel:"noopener noreferrer"}},[t._v("【官方文档 - Java 8 requirement】"),a("OutboundLink")],1)]),t._v(" "),a("p",[t._v("修改安卓项目 "),a("code",[t._v("app")]),t._v(" 目录下的 "),a("code",[t._v("build.gradle")]),t._v(" 文件")]),t._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[t._v("// app/build.gradle\n\nandroid {\n\t...\n compileOptions {\n sourceCompatibility 1.8\n targetCompatibility 1.8\n }\n ...\n}\n")])])]),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110221020.png",alt:""}})]),t._v(" "),a("p",[t._v("修改 "),a("code",[t._v("app/src/main/AndroidManifest.xml")]),t._v(" 文件")]),t._v(" "),a("div",{staticClass:"language-xml extra-class"},[a("pre",{pre:!0,attrs:{class:"language-xml"}},[a("code",[t._v("// app/src/main/AndroidManifest.xml\n\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("activity")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("android:")]),t._v("name")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("io.flutter.embedding.android.FlutterActivity"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("android:")]),t._v("theme")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("@style/AppTheme"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("android:")]),t._v("configChanges")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("android:")]),t._v("hardwareAccelerated")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("true"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("android:")]),t._v("windowSoftInputMode")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("adjustResize"),a("span",{pre:!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("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110221032.png",alt:""}})]),t._v(" "),a("p",[t._v("添加一个按钮,点击弹出 "),a("code",[t._v("Flutter模块")])]),t._v(" "),a("div",{staticClass:"language-xml extra-class"},[a("pre",{pre:!0,attrs:{class:"language-xml"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- activity_main.xml --\x3e")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("Button")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("android:")]),t._v("id")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("@+id/btn"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("android:")]),t._v("layout_width")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("wrap_content"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("android:")]),t._v("layout_height")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("wrap_content"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("android:")]),t._v("textSize")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("20sp"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("android:")]),t._v("text")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("弹出Flutter模块"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("android:")]),t._v("background")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("#000000"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("android:")]),t._v("textColor")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("#ffffff"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("android:")]),t._v("gravity")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),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 "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("android:")]),t._v("onClick")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("btnClick"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("/>")])]),t._v("\n")])])]),a("div",{staticClass:"language-java extra-class"},[a("pre",{pre:!0,attrs:{class:"language-java"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// MainActivity.java")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("public")]),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("btnClick")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("View")]),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("startActivity")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterActivity")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("createDefaultIntent")]),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("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!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:"四、调试与热重载"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#四、调试与热重载"}},[t._v("#")]),t._v(" 四、调试与热重载")]),t._v(" "),a("blockquote",[a("p",[t._v("由于当前我们是使用原生开发工具(如:Xcode)来运行项目,每次修改我们的\n"),a("code",[t._v("Flutter模块")]),t._v(" 的代码,也就需要重新运行才能看到效果,不像之前按下 "),a("code",[t._v("Cmd + s")]),t._v(" 就能进行热重载。这样 "),a("code",[t._v("Flutter模块")]),t._v(" 的开发效率极其低下,那有没有办法可以让我们像之前开发 "),a("code",[t._v("Flutter")]),t._v(" 项目时那样进行 "),a("code",[t._v("热重载")]),t._v(" 呢?答案是有的")])]),t._v(" "),a("p",[a("code",[t._v("Flutter")]),t._v(" 官方提供了 "),a("code",[t._v("flutter attach")]),t._v(" ,以辅助我们开发,到终端下执行")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("flutter attach\n")])])]),a("p",[t._v("如果当前有多个设备,会提示我们需要指定 "),a("code",[t._v("attach")]),t._v(" 哪个设备")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110221047.png",alt:""}})]),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("flutter attach "),a("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-d")]),t._v(" FE305309-9E79-418D-BA3F-7EFECF2980BC\n")])])]),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110221151.png",alt:""}})]),t._v(" "),a("p",[t._v("如图,这样就关联上了,你在 "),a("code",[t._v("dart")]),t._v(" 文件里面对界面进行任何修改后,按 "),a("code",[t._v("r")]),t._v(" 进行热重载,按 "),a("code",[t._v("R")]),t._v(" 进行热启动。")]),t._v(" "),a("p",[t._v("如果你使用的是 "),a("code",[t._v("Android Studio")]),t._v(",可以直接选择对应的设备后,点击右边的 "),a("code",[t._v("Flutter Attach")]),t._v(" 按钮,执行成功后就可以跟之前一样按 "),a("code",[t._v("Cmd + s")]),t._v(" 进行热重载了。")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110221202.png",alt:""}})]),t._v(" "),a("h2",{attrs:{id:"五、资料"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#五、资料"}},[t._v("#")]),t._v(" 五、资料")]),t._v(" "),a("ul",[a("li",[a("p",[t._v("GitHub")]),t._v(" "),a("p",[a("a",{attrs:{href:"https://github.com/LinXunFeng/LXFFlutterHybridDemo",target:"_blank",rel:"noopener noreferrer"}},[t._v("LXFFlutterHybridDemo"),a("OutboundLink")],1)])]),t._v(" "),a("li",[a("p",[t._v("官方文档")]),t._v(" "),a("p",[a("a",{attrs:{href:"https://flutter.dev/docs/development/add-to-app",target:"_blank",rel:"noopener noreferrer"}},[t._v("add-to-app"),a("OutboundLink")],1),t._v(" | "),a("a",{attrs:{href:"https://flutter.dev/docs/development/add-to-app/ios",target:"_blank",rel:"noopener noreferrer"}},[t._v("add-to-app/ios"),a("OutboundLink")],1),t._v(" | "),a("a",{attrs:{href:"https://flutter.dev/docs/development/add-to-app/android",target:"_blank",rel:"noopener noreferrer"}},[t._v("add-to-app/android"),a("OutboundLink")],1),t._v(" | "),a("a",{attrs:{href:"https://flutter.dev/docs/development/add-to-app/debugging",target:"_blank",rel:"noopener noreferrer"}},[t._v("Debugging & hot reload"),a("OutboundLink")],1)])])])])}),[],!1,null,null,null);a.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/118.186377c7.js b/assets/js/118.186377c7.js new file mode 100644 index 000000000..8fea5a17e --- /dev/null +++ b/assets/js/118.186377c7.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[118],{556:function(s,t,e){"use strict";e.r(t);var a=e(8),r=Object(a.a)({},(function(){var s=this,t=s._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":s.$parent.slotKey}},[t("p",[s._v("使用 "),t("code",[s._v("Xcode")]),s._v(" 手动打包是正常的,但是使用脚本打包会报错,错误如下:")]),s._v(" "),t("div",{staticClass:"language-shell extra-class"},[t("pre",{pre:!0,attrs:{class:"language-shell"}},[t("code",[s._v("The following build commands failed:\n\tPhaseScriptExecution "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v("CP-User"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("\\")]),s._v(" Run"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("\\")]),s._v(" Flutter"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("\\")]),s._v(" Build"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("\\")]),s._v(" Script "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("..")]),s._v("./Script-C3A097A8FE12FF5F875B057C.sh\n\t\nflutter build ios "),t("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("--release")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("then")]),s._v(" re-run Archive from Xcode.\nCommand PhaseScriptExecution failed with a nonzero "),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("exit")]),s._v(" code\n")])])]),t("h2",{attrs:{id:"定位错误"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#定位错误"}},[s._v("#")]),s._v(" 定位错误")]),s._v(" "),t("p",[t("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110221247.png",alt:""}})]),s._v(" "),t("p",[s._v("到 "),t("code",[s._v("Flutter")]),s._v(" 环境目录下,按图上所示地址找到 "),t("code",[s._v("xcode_backend.sh")]),s._v(",也可以直接看 "),t("a",{attrs:{href:"https://github.com/flutter/flutter/blob/bcc42901ad6bb3ec644be225b5f9cd998852e0ef/packages/flutter_tools/bin/xcode_backend.sh#L90-L101",target:"_blank",rel:"noopener noreferrer"}},[s._v("官方脚本链接"),t("OutboundLink")],1)]),s._v(" "),t("div",{staticClass:"language-shell extra-class"},[t("pre",{pre:!0,attrs:{class:"language-shell"}},[t("code",[s._v(" "),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# Archive builds (ACTION=install) should always run in release mode.")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("if")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"'),t("span",{pre:!0,attrs:{class:"token variable"}},[s._v("$ACTION")]),s._v('"')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("==")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"install"')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("&&")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"'),t("span",{pre:!0,attrs:{class:"token variable"}},[s._v("$build_mode")]),s._v('"')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("!=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"release"')]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v(";")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("then")]),s._v("\n EchoError "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"========================================================================"')]),s._v("\n EchoError "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"ERROR: Flutter archive builds must be run in Release mode."')]),s._v("\n EchoError "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('""')]),s._v("\n EchoError "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"To correct, ensure FLUTTER_BUILD_MODE is set to release or run:"')]),s._v("\n EchoError "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"flutter build ios --release"')]),s._v("\n EchoError "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('""')]),s._v("\n EchoError "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"then re-run Archive from Xcode."')]),s._v("\n EchoError "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('"========================================================================"')]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("exit")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token parameter variable"}},[s._v("-1")]),s._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[s._v("fi")]),s._v("\n")])])]),t("h2",{attrs:{id:"解决方案"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#解决方案"}},[s._v("#")]),s._v(" 解决方案")]),s._v(" "),t("p",[s._v("可以看到,官方脚本的说明里面给出两个解决方案")]),s._v(" "),t("blockquote",[t("p",[s._v("方案一:直接设置 "),t("code",[s._v("FLUTTER_BUILD_MODE")]),s._v(" 为 "),t("code",[s._v("release")])]),s._v(" "),t("p",[s._v("方案二:先运行 "),t("code",[s._v("flutter build ios --release")]),s._v(" ,再使用 "),t("code",[s._v("Xcode")]),s._v(" 去打包")])]),s._v(" "),t("p",[s._v("这里我们是用 "),t("code",[s._v("Jenkins")]),s._v(" 脚本进行打包,所以方案二不适用,方案一更加方便些")]),s._v(" "),t("div",{staticClass:"language-shell extra-class"},[t("pre",{pre:!0,attrs:{class:"language-shell"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 设置Flutter的编译模式为release")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[s._v("export")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s._v("FLUTTER_BUILD_MODE")]),t("span",{pre:!0,attrs:{class:"token operator"}},[s._v("=")]),s._v("release\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[s._v("# 执行原有项目的打包脚本")]),s._v("\n./script/build_iOS.sh\n")])])])])}),[],!1,null,null,null);t.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/119.b9f44223.js b/assets/js/119.b9f44223.js new file mode 100644 index 000000000..6c4be1f9e --- /dev/null +++ b/assets/js/119.b9f44223.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[119],{431:function(t,s,a){"use strict";a.r(s);var e=a(8),n=Object(e.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[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("iOS14")]),t._v(" ,测试使用 "),s("code",[t._v("Flutter混合开发")]),t._v(" 的线上 "),s("code",[t._v("APP")]),t._v(",没发现什么问题,但是使用 "),s("code",[t._v("Xcode")]),t._v(" 安装"),s("code",[t._v("APP")]),t._v("的场景下,断开 "),s("code",[t._v("Xcode")]),t._v(" 后再运行却闪退了。")]),t._v(" "),s("p",[t._v("公司的 "),s("code",[t._v("APP")]),t._v(" 测试结果如下:")]),t._v(" "),s("table",[s("thead",[s("tr",[s("th",[t._v("APP来源")]),t._v(" "),s("th",[t._v("是否闪退")]),t._v(" "),s("th",[t._v("模式")])])]),t._v(" "),s("tbody",[s("tr",[s("td",[t._v("线上")]),t._v(" "),s("td",[t._v("否")]),t._v(" "),s("td",[t._v("release")])]),t._v(" "),s("tr",[s("td",[t._v("蒲公英")]),t._v(" "),s("td",[t._v("是")]),t._v(" "),s("td",[t._v("debug")])]),t._v(" "),s("tr",[s("td",[t._v("Xcode")]),t._v(" "),s("td",[t._v("是(断开 "),s("code",[t._v("Xcode")]),t._v(" 后再打开 "),s("code",[t._v("APP")]),t._v(" )")]),t._v(" "),s("td",[t._v("debug")])])])]),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("Flutter SDK")]),t._v(", "),s("code",[t._v("Flutter")]),t._v(" 官方的更新速度也是快,对 "),s("code",[t._v("iOS14")]),t._v(" 进行了说明: "),s("a",{attrs:{href:"https://flutter.dev/docs/development/ios-14",target:"_blank",rel:"noopener noreferrer"}},[t._v("Flutter官网说明链接"),s("OutboundLink")],1)]),t._v(" "),s("p",[t._v("大致意思就是说,如果我们在 "),s("code",[t._v("iOS14")]),t._v(" 的真机上安装了 "),s("code",[t._v("debug模式")]),t._v(" 编译出来的 "),s("code",[t._v("flutter")]),t._v(" 应用,那么在断开编译安装连接后,将无法从桌面上打开该应用程序。")]),t._v(" "),s("h3",{attrs:{id:"解决方案"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#解决方案"}},[t._v("#")]),t._v(" 解决方案")]),t._v(" "),s("ol",[s("li",[t._v("再次是使用 "),s("code",[t._v("Xcode")]),t._v(" 或 "),s("code",[t._v("flutter run")]),t._v(" 来运行。")]),t._v(" "),s("li",[t._v("设置 "),s("code",[t._v("Flutter")]),t._v(" 模块的编译模式为 "),s("code",[t._v("profile")]),t._v(" 或 "),s("code",[t._v("release")])])]),t._v(" "),s("h3",{attrs:{id:"补充说明"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#补充说明"}},[t._v("#")]),t._v(" 补充说明")]),t._v(" "),s("ol",[s("li",[t._v("该闪退的情况只发生在真机,并且在模拟器运行的时候, "),s("code",[t._v("Flutter")]),t._v(" 模块的编译模式需要为 "),s("code",[t._v("debug")]),t._v(", 如果设置了 "),s("code",[t._v("release")]),t._v(",编译将会报错。")]),t._v(" "),s("li",[t._v("官方指出如果是 "),s("code",[t._v("纯Flutter项目")]),t._v(" 可以直接使用 "),s("code",[t._v("master channel")]),t._v(" 的 "),s("code",[t._v("Flutter版本")]),t._v(" 秒杀这个问题,但对混合开发并没有该说明,加上我们是使用闲鱼的 "),s("code",[t._v("flutter_boost")]),t._v(" 实现的混合开发,限制了 "),s("code",[t._v("Flutter")]),t._v(" 的版本,所以我也就没有去实践该方案对我们是否可行")])]),t._v(" "),s("h2",{attrs:{id:"二、尝试解决"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#二、尝试解决"}},[t._v("#")]),t._v(" 二、尝试解决")]),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("用 "),s("code",[t._v("Xcode")]),t._v(" 打开工程项目,在 "),s("code",[t._v("Build Settings")]),t._v(" 的最下方找到 "),s("code",[t._v("User-Defined")]),t._v(",点击 "),s("code",[t._v("+")]),t._v(" 按钮,添加一个键为 "),s("code",[t._v("FLUTTER_BUILD_MODE")]),t._v(" ,值为 "),s("code",[t._v("release")]),t._v(" 的配置。")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110221341.png",alt:""}})]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110221358.png",alt:""}})]),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("Xcode")]),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("p",[t._v("问题一:"),s("code",[t._v("release")]),t._v(" 或 "),s("code",[t._v("profile")]),t._v(" 模式下,"),s("code",[t._v("Flutter")]),t._v(" 使用的是 "),s("code",[t._v("AOT")]),t._v(",一些功能不能使用,如:代码断点调试,热重载")]),t._v(" "),s("p",[t._v("问题二:上面也提到了,模拟器只能运行在 "),s("code",[t._v("debug")]),t._v(" 模式下,而我们无法避免会在真机和模拟器之间反复切换运行,每次切换就需要手动调整 "),s("code",[t._v("FLUTTER_BUILD_MODE")]),t._v(" 的值,十分麻烦")]),t._v(" "),s("p",[t._v("那有什么好的办法解决上面遇到的问题呢?")]),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("APP")]),t._v(" 在断开 "),s("code",[t._v("Xcode")]),t._v(" 后无法运行,这个对我们开发者来说不是什么问题,问题是给到测试人员就必须要可以打开才行,包括蒲公英上的包,所以为了节省这些不必要的时间,我们需要自己动手撸一个帮助我们切换 "),s("code",[t._v("Flutter编译模式")]),t._v(" 的脚本。")]),t._v(" "),s("p",[t._v("在修改 "),s("code",[t._v("FLUTTER_BUILD_MODE")]),t._v(" 的值时,我从 "),s("code",[t._v("git")]),t._v(" 中发现,实际上是修改了 "),s("code",[t._v("项目.xcodeproj")]),t._v(",那目前有什么工具可以帮助我们修改 "),s("code",[t._v("xcodeproj")]),t._v(" 文件呢?")]),t._v(" "),s("p",[t._v("这里我找到了"),s("a",{attrs:{href:"https://github.com/kronenthaler/mod-pbxproj",target:"_blank",rel:"noopener noreferrer"}},[t._v("mod-pbxproj"),s("OutboundLink")],1),t._v(",安装和使用在该库的 "),s("code",[t._v("wiki")]),t._v(" 上写的很清楚,这里就不再赘述了,直接上代码")]),t._v(" "),s("div",{staticClass:"language-python extra-class"},[s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" getopt\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" sys\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" pbxproj "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" XcodeProject\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" __name__ "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"__main__"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n argv "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" sys"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("argv"),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("]")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 处理flutter_build_mode")]),t._v("\n flutter_build_mode "),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 boolean"}},[t._v("False")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"release"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# target名称")]),t._v("\n target_name "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("None")]),t._v("\n\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 opts"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" args "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" getopt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("getopt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("argv"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"p:m:t:"')]),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 string"}},[t._v('"path=, mode=, target="')]),s("span",{pre:!0,attrs:{class:"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("except")]),t._v(" getopt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("GetoptError"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('\'switch_flutter_build_mode.py -p "plist文件路径" -m "模式(release|debug)" -t "target名称"\'')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n sys"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("exit"),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(")")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" opt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" arg "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" opts"),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(" opt "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"-p"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"--path"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n project_path "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" arg\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("len")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("project_path"),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("0")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),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("\n sys"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("exit"),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(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" opt "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"-m"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"--mode"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n flutter_build_mode "),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 boolean"}},[t._v("True")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" arg "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("len")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("arg"),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("0")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"release"')]),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(" opt "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"-t"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"--target"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n target_name "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" arg\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 处理flutter")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" flutter_build_mode"),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 punctuation"}},[t._v(":")]),t._v("\n fileName "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" project_path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),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 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 keyword"}},[t._v("not")]),t._v(" fileName"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("endswith"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"xcodeproj"')]),s("span",{pre:!0,attrs:{class:"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("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"请使用-p指定.xcodeproj文件的路径"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n sys"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("exit"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n project "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" XcodeProject"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("load"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("project_path "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'/project.pbxproj'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 设置 User-Defined (如果target_name是None,则每个target都会设置flag)")]),t._v("\n project"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("set_flags"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'FLUTTER_BUILD_MODE'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" flutter_build_mode"),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(",")]),t._v(" target_name"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n project"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("save"),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-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("python switch_flutter_build_mode.py "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-p")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'xxx/项目.xcodeproj'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-t")]),t._v(" target名称 "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-m")]),t._v(" release\n")])])]),s("p",[t._v("各参数说明")]),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("-p")]),t._v(" "),s("td",[s("code",[t._v("xcodeproj")]),t._v(" 文件的路径")])]),t._v(" "),s("tr",[s("td",[t._v("-t")]),t._v(" "),s("td",[s("code",[t._v("target")]),t._v(" 名称")])]),t._v(" "),s("tr",[s("td",[t._v("-m")]),t._v(" "),s("td",[t._v("编译模式 ( "),s("code",[t._v("release")]),t._v("、"),s("code",[t._v("debug")]),t._v("、"),s("code",[t._v("profile")]),t._v(" )")])])])]),t._v(" "),s("p",[t._v("PS: 脚本基于 "),s("code",[t._v("Python3")])]),t._v(" "),s("p",[t._v("我们是使用 "),s("code",[t._v("Jenkins")]),t._v(" 进行打包并自动上传至蒲公英的,所以只需要在 "),s("code",[t._v("Jenkins")]),t._v(" 中配置打包前调用该脚本即可。")]),t._v(" "),s("p",[t._v("最后再结合 "),s("a",{attrs:{href:"http://fitztrev.github.io/shuttle/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Shuttle"),s("OutboundLink")],1),t._v(" 这个软件,就可以实现以界面的方式去切换编译模式了")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210110221411.png",alt:""}})]),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("Flutter混合开发")]),t._v(" 进行说明的,如果有什么不对或不足的地方,欢迎指正,感谢大家的阅读")])])}),[],!1,null,null,null);s.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/12.e8428df9.js b/assets/js/12.e8428df9.js new file mode 100644 index 000000000..f5f272ecf --- /dev/null +++ b/assets/js/12.e8428df9.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[12],{326:function(t,n,s){"use strict";s.r(n);var e=s(8),o=Object(e.a)({},(function(){return(0,this._self._c)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);n.default=o.exports}}]); \ No newline at end of file diff --git a/assets/js/120.c0bd832d.js b/assets/js/120.c0bd832d.js new file mode 100644 index 000000000..46434b992 --- /dev/null +++ b/assets/js/120.c0bd832d.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[120],{432:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[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("Flutter")]),t._v(" 页面,1秒后在 "),s("code",[t._v("Flutter")]),t._v(" 页面上添加一个原生的弹窗视图,代码如下:")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" flutterVc "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterViewController")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("engine"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("fetchFlutterEngine")]),s("span",{pre:!0,attrs:{class:"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(" nibName"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),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(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("navigationController"),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("pushViewController")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("flutterVc"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" animated"),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("\n\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DispatchQueue")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("main"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("asyncAfter")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("deadline"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DispatchTime")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("now")]),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 punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("seconds")]),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(")")]),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 keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v("\n \n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" popView "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFPopView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("frame"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CGRect")]),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 number"}},[t._v("0")]),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(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" width"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" screenW"),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(" screenH"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n flutterVc"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("view"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("addSubview")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("popView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n popView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("checkInfoBlock "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),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 keyword"}},[t._v("weak")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("guard")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),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(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),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("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("navigationController"),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("pushViewController")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("InfoViewController")]),s("span",{pre:!0,attrs:{class:"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(" animated"),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("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n \n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202204040001676.gif",alt:""}})]),t._v(" "),s("p",[t._v("可以看到,我在原生弹窗视图上滑动和点击,会被底下的 "),s("code",[t._v("Flutter")]),t._v(" 内容所响应~")]),t._v(" "),s("p",[t._v("有人会说,直接添加到 "),s("code",[t._v("navigationController")]),t._v(" 的 "),s("code",[t._v("view")]),t._v(" 上不就行了吗?")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("// flutterVc.view.addSubview"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("popView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nself.navigationController?.view.addSubview"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("popView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("不行,因为跳转到其它页面后会遮挡其它页面内容,看效果图便一目了然")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202204040001526.gif",alt:""}})]),t._v(" "),s("p",[t._v("接下来我们一起来看看 "),s("code",[t._v("FlutterViewController")]),t._v(" 源码,便可知道原因了")]),t._v(" "),s("h2",{attrs:{id:"二、源码"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#二、源码"}},[t._v("#")]),t._v(" 二、源码")]),t._v(" "),s("h3",{attrs:{id:"_1、定位匹配的源码"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、定位匹配的源码"}},[t._v("#")]),t._v(" 1、定位匹配的源码")]),t._v(" "),s("p",[t._v("首先找到与当前 "),s("code",[t._v("Flutter")]),t._v(" 环境相匹配的源码内容")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("➜ flutter doctor "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-v")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("✓"),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("Channel stable, "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2.10")]),t._v(".4, on macOS "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("12.2")]),t._v(".1 21D62 darwin-x64, locale\n zh-Hans-CN"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n • Flutter version "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2.10")]),t._v(".4 at /Users/lxf/developer/flutter\n • Upstream repository https://github.com/flutter/flutter.git\n • Framework revision c860cba910 "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("9")]),t._v(" days ago"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2022")]),t._v("-03-25 00:23:12 "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-0500")]),t._v("\n • Engine revision 57d3bac3dd\n • Dart version "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2.16")]),t._v(".2\n • DevTools version "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2.9")]),t._v(".2\n • Pub download mirror https://pub.flutter-io.cn\n • Flutter download mirror https://storage.flutter-io.cn\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".\n")])])]),s("p",[t._v("从此处可以拿到 "),s("code",[t._v("Engine")]),t._v(" 的 "),s("code",[t._v("commit id")])]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("Engine revision 57d3bac3dd\n")])])]),s("p",[t._v("将下方链接中的 "),s("code",[t._v("【commit id】")]),t._v(" 进行替换即可得到相匹配的源码链接了")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("https://github.com/flutter/engine/blob/【commit id】/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm\n")])])]),s("p",[t._v("链接: "),s("a",{attrs:{href:"https://github.com/flutter/engine/blob/57d3bac3dd/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm",target:"_blank",rel:"noopener noreferrer"}},[t._v("engine/FlutterViewController.mm at 57d3bac3dd"),s("OutboundLink")],1)]),t._v(" "),s("h3",{attrs:{id:"_2、定位造成问题的源码"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、定位造成问题的源码"}},[t._v("#")]),t._v(" 2、定位造成问题的源码")]),t._v(" "),s("p",[t._v("经过源码的查看,可以很快定位到如下部分的内容:")]),t._v(" "),s("div",{staticClass:"language-objc extra-class"},[s("pre",{pre:!0,attrs:{class:"language-objc"}},[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(".")]),t._v(" \n\n"),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("void")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("touchesBegan"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("NSSet"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("touches withEvent"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("UIEvent"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("event "),s("span",{pre:!0,attrs:{class:"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("self")]),t._v(" dispatchTouches"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("touches pointerDataChangeOverride"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("nullptr event"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("event"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 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("void")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("touchesMoved"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("NSSet"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("touches withEvent"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("UIEvent"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("event "),s("span",{pre:!0,attrs:{class:"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("self")]),t._v(" dispatchTouches"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("touches pointerDataChangeOverride"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("nullptr event"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("event"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 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("void")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("touchesEnded"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("NSSet"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("touches withEvent"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("UIEvent"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("event "),s("span",{pre:!0,attrs:{class:"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("self")]),t._v(" dispatchTouches"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("touches pointerDataChangeOverride"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("nullptr event"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("event"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 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("void")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("touchesCancelled"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("NSSet"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("touches withEvent"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("UIEvent"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("event "),s("span",{pre:!0,attrs:{class:"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("self")]),t._v(" dispatchTouches"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("touches pointerDataChangeOverride"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("nullptr event"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("event"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 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("void")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("forceTouchesCancelled"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("NSSet"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("touches "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n flutter"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("PointerData"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Change cancel "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" flutter"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("PointerData"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Change"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("kCancel"),s("span",{pre:!0,attrs:{class:"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("self")]),t._v(" dispatchTouches"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("touches pointerDataChangeOverride"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("cancel event"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("nullptr"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 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("// Dispatches the UITouches to the engine. Usually, the type of change of the touch is determined")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// from the UITouch's phase. However, FlutterAppDelegate fakes touches to ensure that touch events")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// in the status bar area are available to framework code. The change type (optional) of the faked")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// touch is specified in the second argument.")]),t._v("\n"),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("void")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("dispatchTouches"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("NSSet"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("touches\n pointerDataChangeOverride"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("flutter"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("PointerData"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Change"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("overridden_change\n event"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("UIEvent"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("event "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("FlutterViewController")]),t._v(" 内部,重写了 "),s("code",[t._v("touchesXXX")]),t._v(" 系列的方法,然后统一调用 "),s("code",[t._v("- dispatchTouches:pointerDataChangeOverride:event:")]),t._v(" 方法,将 "),s("code",[t._v("UITouches")]),t._v(" 分发至 "),s("code",[t._v("Flutter")]),t._v(" 引擎,从而与 "),s("code",[t._v("Flutter")]),t._v(" 内容进行交互")]),t._v(" "),s("p",[t._v("这便是我们在原生弹窗上的点击、拖拽操作会被 "),s("code",[t._v("Flutter")]),t._v(" 内容所响应的原因。")]),t._v(" "),s("h2",{attrs:{id:"三、解决问题"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#三、解决问题"}},[t._v("#")]),t._v(" 三、解决问题")]),t._v(" "),s("p",[t._v("既然我们已经知道原因所在,现在就好想办法去解决这个问题了")]),t._v(" "),s("p",[t._v("这里我直接给出最终实现代码:")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// LXFFlutterViewController.swift")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Foundation")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Flutter")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("protocol")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFFlutterForbidResponseProtocol")]),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\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("LXFFlutterViewController")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterViewController")]),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("var")]),t._v(" isForbidResponseForFlutter "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),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("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("touchesBegan")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token omit keyword"}},[t._v("_")]),t._v(" touches"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Set")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UITouch")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" with event"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIEvent")]),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("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("isForbidResponse")]),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("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isForbidResponseForFlutter "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),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("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"touches began"')])]),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("touchesBegan")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("touches"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" with"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" event"),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("override")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("touchesMoved")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token omit keyword"}},[t._v("_")]),t._v(" touches"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Set")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UITouch")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" with event"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIEvent")]),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("if")]),t._v(" isForbidResponseForFlutter "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"touches move"')])]),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("touchesMoved")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("touches"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" with"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" event"),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("override")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("touchesEnded")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token omit keyword"}},[t._v("_")]),t._v(" touches"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Set")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UITouch")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" with event"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIEvent")]),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("if")]),t._v(" isForbidResponseForFlutter "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isForbidResponseForFlutter "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),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("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"touches ended"')])]),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("touchesEnded")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("touches"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" with"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" event"),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("override")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("touchesCancelled")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token omit keyword"}},[t._v("_")]),t._v(" touches"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Set")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UITouch")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" with event"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIEvent")]),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("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("isForbidResponse")]),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("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isForbidResponseForFlutter "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),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("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"touches cacelled"')])]),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("touchesCancelled")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("touches"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" with"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" event"),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 keyword"}},[t._v("extension")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFFlutterViewController")]),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("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("isForbidResponse")]),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 class-name"}},[t._v("Bool")]),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(" subViews "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("view"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("subviews "),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 subViews "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" subViews"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("reversed")]),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("for")]),t._v(" i "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("..<")]),t._v("subViews"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" subView "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" subViews"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),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 keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("isHadForbidResponseView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("view"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" subView"),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 boolean"}},[t._v("true")]),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 keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),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("fileprivate")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("isHadForbidResponseView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("view"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIView")]),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 class-name"}},[t._v("Bool")]),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(" view "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("is")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFFlutterForbidResponseProtocol")]),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 boolean"}},[t._v("true")]),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("let")]),t._v(" subViews "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" view"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("subviews\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("..<")]),t._v("subViews"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" subView "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" subViews"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),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 keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("isHadForbidResponseView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("view"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" subView"),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 boolean"}},[t._v("true")]),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 keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),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",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202204040002197.png",alt:""}})]),t._v(" "),s("p",[t._v("上图可以看到,在 "),s("code",[t._v("FlutterView")]),t._v(" 中,"),s("code",[t._v("LXFPopView")]),t._v(" 这一类的弹窗视图一般都会在使用时才会插入到视图中,所以在 "),s("code",[t._v("isForbidResponse")]),t._v(" 方法里进行反转遍历子视图,以减少遍历次数。")]),t._v(" "),s("p",[s("code",[t._v("touchesMoved")]),t._v(" 调用次数较多,所以为了避免在 "),s("code",[t._v("touchesMoved")]),t._v(" 中去高频率的遍历 "),s("code",[t._v("subViews")]),t._v(",这里使用了 "),s("code",[t._v("isForbidResponseForFlutter")]),t._v(" 变量,在 "),s("code",[t._v("touchesBegan")]),t._v(" 时判断并记录是否需要禁用 "),s("code",[t._v("Flutter")]),t._v(" 内容响应触摸事件,在 "),s("code",[t._v("touchesEnded")]),t._v(" 和 "),s("code",[t._v("touchesCancelled")]),t._v(" 中对 "),s("code",[t._v("isForbidResponseForFlutter")]),t._v(" 重置为 "),s("code",[t._v("false")])]),t._v(" "),s("h2",{attrs:{id:"四、使用步骤"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#四、使用步骤"}},[t._v("#")]),t._v(" 四、使用步骤")]),t._v(" "),s("p",[t._v("步骤一:")]),t._v(" "),s("p",[t._v("使用 "),s("code",[t._v("LXFFlutterViewController")])]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" flutterVc "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFFlutterViewController")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("engine"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("fetchFlutterEngine")]),s("span",{pre:!0,attrs:{class:"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(" nibName"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),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(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("步骤二:")]),t._v(" "),s("p",[t._v("令弹窗视图所在类遵守协议:"),s("code",[t._v("LXFFlutterForbidResponseProtocol")])]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extension")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFPopView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFFlutterForbidResponseProtocol")]),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("p",[t._v("看看效果如何:")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202204040002380.gif",alt:""}})]),t._v(" "),s("p",[t._v("完美 😃")]),t._v(" "),s("p",[t._v("最后附上 "),s("code",[t._v("Demo")]),t._v(" 链接:"),s("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_hybrid_touch_response_demo",target:"_blank",rel:"noopener noreferrer"}},[t._v("LinXunFeng/flutter_hybrid_touch_response_demo (github.com)"),s("OutboundLink")],1)])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/121.997c5f8d.js b/assets/js/121.997c5f8d.js new file mode 100644 index 000000000..4c767aeb9 --- /dev/null +++ b/assets/js/121.997c5f8d.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[121],{433:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h2",{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",[t._v("一进入页面显示的是品牌广告视图(代号: "),s("code",[t._v("A")]),t._v("),上拉超过一定的距离后,向上翻到相册视图(代号: "),s("code",[t._v("B")]),t._v(")")]),t._v(" "),s("li",[t._v("下拉超过一定距离后切换为 "),s("code",[t._v("A")]),t._v(",快速下拉回到顶部的情况下不会切换到 "),s("code",[t._v("A")]),t._v(" !")])]),t._v(" "),s("p",[t._v("这里的 "),s("code",[t._v("A")]),t._v(" 和 "),s("code",[t._v("B")]),t._v(" 对应的就是下图效果图中的 "),s("code",[t._v("AAA")]),t._v(" 和 "),s("code",[t._v("BBB")]),t._v(" 视图")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202204172014548.gif",alt:""}})]),t._v(" "),s("h2",{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 keyword"}},[t._v("enum")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VerticalFlipShowingType")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n aaa"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n bbb"),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("headerFlipShowingType")]),t._v(" 用来记录当前 "),s("code",[t._v("header")]),t._v(" 正在显示的类型,默认为 "),s("code",[t._v("aaa")])]),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("/// 当前header翻页视图显示的视图类型")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VerticalFlipShowingType")]),t._v(" headerFlipShowingType "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VerticalFlipShowingType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("aaa"),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 class-name"}},[t._v("Scaffold")]),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 class-name"}},[t._v("CustomScrollView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n slivers"),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 \n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverToBoxAdapter")]),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(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildHeaderWidget")]),s("span",{pre:!0,attrs:{class:"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("// header(AAA + BBB)")]),t._v("\n \n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverList")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 正常的列表 (显示:index -- 下标)")]),t._v("\n delegate"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverChildBuilderDelegate")]),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("ctx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" index"),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 class-name"}},[t._v("ListTile")]),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 class-name"}},[t._v("Text")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"index -- ')]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("$")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("index")])]),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 "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n childCount"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("40")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 \n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 右下角的浮动按钮,用来切换 header")]),t._v("\n floatingActionButton"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),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(" "),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 headerFlipShowingType "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n headerFlipShowingType "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VerticalFlipShowingType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("aaa\n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VerticalFlipShowingType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bbb\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VerticalFlipShowingType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("aaa"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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(".")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("header")]),t._v(",正常的列表数据,以及右下角按钮,点击按钮以测试切换效果")]),t._v(" "),s("p",[t._v("接下来是 "),s("code",[t._v("header")]),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 class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildHeaderWidget")]),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 class-name"}},[t._v("SizedBox")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 限制 Stack 的高度,否则报错")]),t._v("\n height"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" headerFlipShowingType "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VerticalFlipShowingType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("aaa\n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" headerFlipHeightA\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" headerFlipHeightB\n child"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Stack")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n clipBehavior"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Clip")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("none"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 子视图超出 Stack 不裁剪,依旧显示")]),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 class-name"}},[t._v("Positioned")]),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(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n right"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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 bottom"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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 height"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" headerFlipHeight"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// AAA 与 BBB 加起来的总高度")]),t._v("\n child"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ListView")]),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 class-name"}},[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(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 消除顶部刘海造成的安全区域偏移")]),t._v("\n physics"),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 class-name"}},[t._v("NeverScrollableScrollPhysics")]),s("span",{pre:!0,attrs:{class:"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 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 class-name"}},[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(" headerFlipHeightA"),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 class-name"}},[t._v("HeaderFlipWidget")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n text"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"AAA"')])]),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Color")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0xFFFA5252")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 class-name"}},[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(" headerFlipHeightB"),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 class-name"}},[t._v("HeaderFlipWidget")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n text"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"BBB"')])]),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Color")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0xFF228BE6")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("ul",[s("li",[t._v("在 "),s("code",[t._v("header")]),t._v(" 中,"),s("code",[t._v("AAA")]),t._v(" 和 "),s("code",[t._v("BBB")]),t._v(" 视图都是存放到 "),s("code",[t._v("ListView")])]),t._v(" "),s("li",[t._v("包裹"),s("code",[t._v("Stack")]),t._v(" 的 "),s("code",[t._v("SizedBox")]),t._v(" 就是为了控制 "),s("code",[t._v("header")]),t._v(" 的高度,且 "),s("code",[t._v("header")]),t._v(" 的高度会跟随当前展示的类型而改变,如:当前展示 "),s("code",[t._v("AAA")]),t._v(",则高度为 "),s("code",[t._v("AAA")]),t._v(" 视图的高度,展示 "),s("code",[t._v("BBB")]),t._v(" 时,则高度为 "),s("code",[t._v("BBB")]),t._v(" 视图的高度")]),t._v(" "),s("li",[t._v("使用 "),s("code",[t._v("Stack")]),t._v(" + "),s("code",[t._v("Positioned")]),t._v(" 这个组合,将 "),s("code",[t._v("Positioned")]),t._v(" 的 "),s("code",[t._v("bottom")]),t._v(" 设置为 "),s("code",[t._v("0")]),t._v(",即 "),s("code",[t._v("ListView")]),t._v(" 的底部紧贴 "),s("code",[t._v("Stack")]),t._v(" 的底部,使得 "),s("code",[t._v("ListView")]),t._v(" 只能向上超出 "),s("code",[t._v("Stack")])]),t._v(" "),s("li",[s("code",[t._v("Stack")]),t._v(" 设置了 "),s("code",[t._v("clipBehavior")]),t._v(" 为 "),s("code",[t._v("Clip.none")]),t._v(",因此在正在显示 "),s("code",[t._v("BBB")]),t._v(" 视图的情况下,下拉列表可以看到 "),s("code",[t._v("AAA")]),t._v(" 的视图内容")]),t._v(" "),s("li",[t._v("固定 "),s("code",[t._v("Positioned")]),t._v(" 的 高度为 "),s("code",[t._v("AAA")]),t._v(" 和 "),s("code",[t._v("BBB")]),t._v(" 视图的总高,目的是为了结合后面提及的透明 "),s("code",[t._v("Container")]),t._v("(或者说 "),s("code",[t._v("AnimatedContainer B")]),t._v("), 方便控制 "),s("code",[t._v("BBB")]),t._v(" 视图的显示")])]),t._v(" "),s("p",[t._v("如果这里还不太理解 "),s("code",[t._v("AAA")]),t._v(" 和 "),s("code",[t._v("BBB")]),t._v(" 的显示逻辑也不用担心,下面会进行详细说明")]),t._v(" "),s("p",[t._v("我们来看看此时的效果:")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202204172015631.gif",alt:""}})]),t._v(" "),s("p",[t._v("为了完整显示 "),s("code",[t._v("AAA")]),t._v(" 视图,且不显示 "),s("code",[t._v("BBB")]),t._v(" 视图,在 "),s("code",[t._v("AAA")]),t._v(" 的前边添加一个透明的 "),s("code",[t._v("Container")]),t._v(",并把高度设置为 "),s("code",[t._v("BBB")]),t._v(" 的高度(显示 "),s("code",[t._v("BBB")]),t._v(" 视图时,透明的 "),s("code",[t._v("Container")]),t._v(" 的高度设置为 "),s("code",[t._v("0")]),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 class-name"}},[t._v("ListView")]),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 class-name"}},[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(",")]),t._v("\n physics"),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 class-name"}},[t._v("NeverScrollableScrollPhysics")]),s("span",{pre:!0,attrs:{class:"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 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 class-name"}},[t._v("Container")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 添加了透明的 Container")]),t._v("\n height"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" headerFlipShowingType "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VerticalFlipShowingType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("aaa\n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" headerFlipHeightB\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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 "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("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(" headerFlipHeightA"),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 class-name"}},[t._v("HeaderFlipWidget")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n text"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"AAA"')])]),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Color")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0xFFFA5252")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 class-name"}},[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(" headerFlipHeightB"),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 class-name"}},[t._v("HeaderFlipWidget")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n text"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"BBB"')])]),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Color")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0xFF228BE6")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("我们再来看看此时的效果:")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202204172015945.gif",alt:""}})]),t._v(" "),s("p",[t._v("可以,不过变化很僵硬,加点动画就可以了")]),t._v(" "),s("p",[t._v("完整的 "),s("code",[t._v("header")]),t._v(" 的构建方法代码如下")]),t._v(" "),s("p",[t._v("改动的地方在 "),s("code",[t._v("AnimatedContainer A")]),t._v(" 和 "),s("code",[t._v("AnimatedContainer B")]),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 class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildHeaderWidget")]),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(" duration "),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 class-name"}},[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("200")]),s("span",{pre:!0,attrs:{class:"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("// AnimatedContainer A: 控制整个header以动画的方式改变高度")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[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(" headerFlipShowingType "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VerticalFlipShowingType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("aaa\n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" headerFlipHeightA\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" headerFlipHeightB"),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 class-name"}},[t._v("Stack")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n clipBehavior"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Clip")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("none"),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 class-name"}},[t._v("Positioned")]),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(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n right"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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 bottom"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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 height"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" headerFlipHeight"),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 class-name"}},[t._v("ListView")]),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 class-name"}},[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(",")]),t._v("\n physics"),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 class-name"}},[t._v("NeverScrollableScrollPhysics")]),s("span",{pre:!0,attrs:{class:"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 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 comment"}},[t._v("// AnimatedContainer B:控制 AAA 和 BBB 切换时的互推效果")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[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(" headerFlipShowingType "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VerticalFlipShowingType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("aaa\n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" headerFlipHeightB\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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 "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("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(" headerFlipHeightA"),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 class-name"}},[t._v("HeaderFlipWidget")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n text"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"AAA"')])]),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Color")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0xFFFA5252")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 class-name"}},[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(" headerFlipHeightB"),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 class-name"}},[t._v("HeaderFlipWidget")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n text"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"BBB"')])]),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Color")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0xFF228BE6")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("ul",[s("li",[s("code",[t._v("AnimatedContainer A")]),t._v(": 控制整个 "),s("code",[t._v("header")]),t._v(" 以动画的方式改变高度")]),t._v(" "),s("li",[s("code",[t._v("AnimatedContainer B")]),t._v(": 控制 "),s("code",[t._v("AAA")]),t._v(" 和 "),s("code",[t._v("BBB")]),t._v(" 切换时的互推效果(切到 "),s("code",[t._v("AAA")]),t._v(" 时,将 "),s("code",[t._v("BBB")]),t._v(" 往下推,切到 "),s("code",[t._v("BBB")]),t._v(" 时,将 "),s("code",[t._v("AAA")]),t._v(" 往上推)")])]),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("AAA")]),t._v(" 和 "),s("code",[t._v("BBB")]),t._v(" 的显示逻辑,我做了如下一张图")]),t._v(" "),s("ul",[s("li",[t._v("从左往右看:便是上拉时,从 "),s("code",[t._v("AAA")]),t._v(" 切换到 "),s("code",[t._v("BBB")]),t._v(" 的过程")]),t._v(" "),s("li",[t._v("从右往左看:便是下拉时,从 "),s("code",[t._v("BBB")]),t._v(" 切换到 "),s("code",[t._v("AAA")]),t._v(" 的过程")])]),t._v(" "),s("p",[t._v("整个过程最主要的就是 "),s("code",[t._v("AnimatedContainer A")]),t._v(" 和 "),s("code",[t._v("AnimatedContainer B")]),t._v(" 的高度变化")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202204172016643.png",alt:""}})]),t._v(" "),s("ul",[s("li",[t._v("前置条件:"),s("code",[t._v("Positioned")]),t._v(" 的 高度(即 "),s("code",[t._v("ListView")]),t._v(" 的高度)为 "),s("code",[t._v("AAA")]),t._v(" 和 "),s("code",[t._v("BBB")]),t._v(" 视图的总高")]),t._v(" "),s("li",[t._v("当显示 "),s("code",[t._v("AAA")]),t._v(" 时,即图一,"),s("code",[t._v("Stack")]),t._v(" 的高度为 "),s("code",[t._v("AAA")]),t._v(" 的高度,"),s("code",[t._v("AnimatedContainer B")]),t._v(" 的高度为 "),s("code",[t._v("BBB")]),t._v("的高度,此时的 "),s("code",[t._v("BBB")]),t._v(" 视图超出了 "),s("code",[t._v("ListView")]),t._v(" 的视窗,所以其不显示,注意看图一半透明的 "),s("code",[t._v("BBB")])]),t._v(" "),s("li",[t._v("在切换显示 "),s("code",[t._v("BBB")]),t._v(" 的过程中,即图二到图三,"),s("code",[t._v("AnimatedContainer B")]),t._v(" 的高度不断减小至 "),s("code",[t._v("0")]),t._v(",使得 "),s("code",[t._v("BBB")]),t._v(" 逐渐进入 "),s("code",[t._v("ListView")]),t._v(" 的视窗内,直到完全展示,"),s("code",[t._v("Stack")]),t._v(" 的高度也从 "),s("code",[t._v("AAA")]),t._v(" 的高度减小至 "),s("code",[t._v("BBB")]),t._v(" 的高度,使得 "),s("code",[t._v("AAA")]),t._v(" 超出 "),s("code",[t._v("ListView")]),t._v(" 的视窗,这样便只会展示 "),s("code",[t._v("BBB")]),t._v(" 了")])]),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("header")]),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("final")]),t._v(" double headerFlipThreshold "),s("span",{pre:!0,attrs:{class:"token operator"}},[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\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 可监听滚动,控制偏移量")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ScrollController")]),t._v(" scrollController "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ScrollController")]),s("span",{pre:!0,attrs:{class:"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("CustomScrollView")]),t._v(" 中传入 "),s("code",[t._v("scrollController")]),t._v(",并用 "),s("code",[t._v("NotificationListener")]),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 class-name"}},[t._v("Scaffold")]),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 class-name"}},[t._v("NotificationListener")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ScrollNotification")]),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 onNotification"),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 class-name"}},[t._v("ScrollNotification")]),t._v(" notification"),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("_handleListScroll")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("notification"),s("span",{pre:!0,attrs:{class:"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 boolean"}},[t._v("false")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// return true 会导致进度条将失效")]),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 class-name"}},[t._v("CustomScrollView")]),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(" scrollController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 传入 scrollController")]),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 floatingActionButton"),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"),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 keyword"}},[t._v("void")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_handleListScroll")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ScrollNotification")]),t._v(" notification"),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(" offset "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" notification"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("metrics"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("pixels"),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("headerFlipShowingType "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VerticalFlipShowingType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("aaa"),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("offset "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" headerFlipThreshold"),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 headerFlipShowingType "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VerticalFlipShowingType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bbb"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 scrollController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("jumpTo")]),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 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 "),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("offset "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("headerFlipThreshold"),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 headerFlipShowingType "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VerticalFlipShowingType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("aaa"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 scrollController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("jumpTo")]),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 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("ul",[s("li",[t._v("根据偏移量是否超过阈值,来控制是否切换 "),s("code",[t._v("header")])]),t._v(" "),s("li",[t._v("当切换 "),s("code",[t._v("header")]),t._v(" 后,我们需要使用 "),s("code",[t._v("scrollController")]),t._v(" 来设置列表的偏移量为 "),s("code",[t._v("0")])])]),t._v(" "),s("p",[t._v("到这里也就差不多了,不过,现在还有个细节需要处理,请看图")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20210320170901/image/202204172016591.gif",alt:""}})]),t._v(" "),s("p",[t._v("如图操作所示,快速下拉滚动会直接从 "),s("code",[t._v("BBB")]),t._v(" 切换到 "),s("code",[t._v("AAA")]),t._v(",这并不是我们想要的。")]),t._v(" "),s("p",[t._v("我们想要的是:当前正在显示 "),s("code",[t._v("BBB")]),t._v(",且从最顶部开始下拉才能切到 "),s("code",[t._v("AAA")])]),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 comment"}},[t._v("/// 是否是从最顶部开始滚动的")]),t._v("\nbool isStartScrollAtTop "),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("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("_handleListScroll")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ScrollNotification")]),t._v(" notification"),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(" offset "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" notification"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("metrics"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("pixels"),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("notification "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("is")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ScrollStartNotification")]),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 isStartScrollAtTop "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" offset "),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(" "),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("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("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("headerFlipShowingType "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VerticalFlipShowingType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("aaa"),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("offset "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" headerFlipThreshold"),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 headerFlipShowingType "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VerticalFlipShowingType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bbb"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 scrollController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("jumpTo")]),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 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 "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("isStartScrollAtTop"),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("// 不是从最顶部开始滚动,则不切换到 AAA")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),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("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("offset "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("headerFlipThreshold"),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 headerFlipShowingType "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VerticalFlipShowingType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("aaa"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 scrollController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("jumpTo")]),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 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("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("大功告成,最后附上 "),s("code",[t._v("Demo")]),t._v(" 链接:"),s("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_demo/blob/main/lib/features/vertical_flip/vertical_flip_page.dart",target:"_blank",rel:"noopener noreferrer"}},[t._v("flutter_demo/vertical_flip_page.dart (github.com)"),s("OutboundLink")],1)])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/122.7f4b0ebb.js b/assets/js/122.7f4b0ebb.js new file mode 100644 index 000000000..34f8dd283 --- /dev/null +++ b/assets/js/122.7f4b0ebb.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[122],{434:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h2",{attrs:{id:"一、概述"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#一、概述"}},[t._v("#")]),t._v(" 一、概述")]),t._v(" "),s("p",[s("code",[t._v("Flutter")]),t._v(" 中的 "),s("code",[t._v("ListView")]),t._v(" 相信大家都用的很熟了,不过有没有人遇到过一些这样的需求:")]),t._v(" "),s("ul",[s("li",[t._v("详情页滚动到某一指定模块后,停止滚动并根据该指定模块的大小弹出全屏新手引导")]),t._v(" "),s("li",[t._v("详情页在滚动过程中,顶部的模块定位导航栏需要及时更新指示器下标")]),t._v(" "),s("li",[t._v("视频列表在滚动过程中,适当位置的子部件会自动进行播放视频")]),t._v(" "),s("li",[t._v("等等")])]),t._v(" "),s("p",[t._v("在日常开发过程中这种类似的功能需求还是蛮多的,因此我封装了一个库:"),s("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("flutter_scrollview_observer"),s("OutboundLink")],1)]),t._v(" "),s("p",[t._v("相信可以很好的帮助大家解决这些问题 😃")]),t._v(" "),s("h2",{attrs:{id:"二、应用场景"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#二、应用场景"}},[t._v("#")]),t._v(" 二、应用场景")]),t._v(" "),s("p",[t._v("下面我们来看看常见的应用场景:")]),t._v(" "),s("h3",{attrs:{id:"_1、获取最顶部的子部件信息"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、获取最顶部的子部件信息"}},[t._v("#")]),t._v(" 1、获取最顶部的子部件信息")]),t._v(" "),s("p",[t._v("可以获取当前的第一个子部件和所有正在显示的子部件信息")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202205291628046.gif",alt:""}})]),t._v(" "),s("h3",{attrs:{id:"_2、视频列表自动播放"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、视频列表自动播放"}},[t._v("#")]),t._v(" 2、视频列表自动播放")]),t._v(" "),s("p",[t._v("当子部件进入列表中间区域时,自动播放视频")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202205291628769.gif",alt:""}})]),t._v(" "),s("h3",{attrs:{id:"_3、模块定位"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_3、模块定位"}},[t._v("#")]),t._v(" 3、模块定位")]),t._v(" "),s("p",[t._v("当滚动到一些特定模块时,顶部的 "),s("code",[t._v("TabBar")]),t._v(" 的指示器切换到对应模块 "),s("code",[t._v("tab")])]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202205291628102.gif",alt:""}})]),t._v(" "),s("h2",{attrs:{id:"三、使用"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#三、使用"}},[t._v("#")]),t._v(" 三、使用")]),t._v(" "),s("h3",{attrs:{id:"_1、基本使用"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、基本使用"}},[t._v("#")]),t._v(" 1、基本使用")]),t._v(" "),s("p",[t._v("创建 "),s("code",[t._v("ListView")]),t._v(",并在其 "),s("code",[t._v("builder")]),t._v(" 回调中,将 "),s("code",[t._v("SliverListView")]),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 class-name"}},[t._v("BuildContext")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" _sliverListViewContext"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 class-name"}},[t._v("ListView")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildListView")]),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 class-name"}},[t._v("ListView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("separated")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n itemBuilder"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" index"),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("// 在 builder 回调中,将 BuildContext 记录起来")]),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("_sliverListViewContext "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" ctx"),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 _sliverListViewContext "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ctx"),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(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildListItemView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 separatorBuilder"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" index"),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("_buildSeparatorView")]),s("span",{pre:!0,attrs:{class:"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 itemCount"),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 "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("SliverListView")]),t._v(" 的 "),s("code",[t._v("BuildContext")]),t._v(","),s("code",[t._v("ListView")]),t._v(" 最终也是使用 "),s("code",[t._v("SliverListView")]),t._v(" 来进行布局的")])]),t._v(" "),s("p",[t._v("构建 "),s("code",[t._v("ListViewObserver")])]),t._v(" "),s("ul",[s("li",[s("code",[t._v("child")]),t._v(": 将构建的 "),s("code",[t._v("ListView")]),t._v(" 做为 "),s("code",[t._v("ListViewObserver")]),t._v(" 的子部件")]),t._v(" "),s("li",[s("code",[t._v("sliverListContexts")]),t._v(": 该回调中需要返回被观察的 "),s("code",[t._v("ListView")]),t._v(" 的 "),s("code",[t._v("BuildContext")])]),t._v(" "),s("li",[s("code",[t._v("onObserve")]),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 class-name"}},[t._v("ListViewObserver")]),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("_buildListView")]),s("span",{pre:!0,attrs:{class:"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 sliverListContexts"),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 punctuation"}},[t._v("[")]),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("_sliverListViewContext "),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(" _sliverListViewContext"),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(";")]),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 onObserve"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("resultMap"),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(" model "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" resultMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("_sliverListViewContext"),s("span",{pre:!0,attrs:{class:"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("model "),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 keyword"}},[t._v("return")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'firstChild.index -- ")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("${")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("model"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("firstChild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("index")]),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\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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'displaying -- ")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("${")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("model"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("displayingChildIndexList")]),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 "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("除了上述几个常用参数外,还有:")]),t._v(" "),s("ul",[s("li",[s("code",[t._v("leadingOffset")]),t._v(":顶部偏移,当列表的视窗会固定被其它视图挡住时使用")]),t._v(" "),s("li",[s("code",[t._v("dynamicLeadingOffset")]),t._v(":动态顶部偏移,当列表的视窗会动态被其它视图挡住时使用")])]),t._v(" "),s("p",[t._v("这里看一下图就明白了")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202205291629435.gif",alt:""}})]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("// 导航栏半透明\nflutter: firstChild.index -- "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\nflutter: displaying -- "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("6")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("7")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("8")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("9")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("11")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\nflutter: firstChild.index -- "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\nflutter: displaying -- "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("6")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("7")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("8")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("9")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("11")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n\n// 导航栏完全不透明\nflutter: firstChild.index -- "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v("\nflutter: displaying -- "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("6")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("7")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("8")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("9")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("11")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n")])])]),s("p",[t._v("滚动过程会改变顶部的导航栏透明度,在这个前提下:")]),t._v(" "),s("ol",[s("li",[t._v("当半透明时,我们希望列表的所有可见子部件从最顶部开始算起")]),t._v(" "),s("li",[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 class-name"}},[t._v("ListViewObserver")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 dynamicLeadingOffset"),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("_navBgAlpha "),s("span",{pre:!0,attrs:{class:"token operator"}},[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 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 keyword"}},[t._v("return")]),t._v(" _safeAreaPaddingTop "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" _navContentHeight"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("ul",[s("li",[s("code",[t._v("toNextOverPercent")]),t._v(":内部逻辑在取到第一个子部件后,如果该子部件被挡住的大小与自身大小的比例超过了该值,则会取下一个子部件为第一个子部件。")])]),t._v(" "),s("h3",{attrs:{id:"_2、手动触发"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、手动触发"}},[t._v("#")]),t._v(" 2、手动触发")]),t._v(" "),s("p",[t._v("默认是 "),s("code",[t._v("ListView")]),t._v(" 在滚动的时候才会观察到相关数据。")]),t._v(" "),s("p",[t._v("如果需要在非滚动状态下进行一次观察,可以使用 "),s("code",[t._v("ListViewOnceObserveNotification")]),t._v(" 进行手动触发")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("ListViewOnceObserveNotification().dispatch(_sliverListViewContext);\n")])])]),s("blockquote",[s("p",[t._v("注:如果频繁触发,且观察结果相同,则 "),s("code",[t._v("onObserve")]),t._v(" 只会回调一次")])]),t._v(" "),s("h3",{attrs:{id:"_3、子部件信息"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_3、子部件信息"}},[t._v("#")]),t._v(" 3、子部件信息")]),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("class")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ListViewObserveModel")]),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("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ListViewObserveDisplayingChildModel")]),t._v(" firstChild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 正在显示的所有子部件模型数据")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ListViewObserveDisplayingChildModel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" displayingChildModelList"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 正在显示的所有子部件下标")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("int"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("get")]),t._v(" displayingChildIndexList "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n displayingChildModelList"),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 operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" e"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("index"),s("span",{pre:!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("}")]),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 keyword"}},[t._v("class")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ListViewObserveDisplayingChildModel")]),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("final")]),t._v(" int index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 子部件的 RenderObject")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("RenderBox")]),t._v(" renderObject"),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("GitHub: "),s("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("LinXunFeng/flutter_scrollview_observer"),s("OutboundLink")],1)])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/123.b52eea15.js b/assets/js/123.b52eea15.js new file mode 100644 index 000000000..b5c8f7e1c --- /dev/null +++ b/assets/js/123.b52eea15.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[123],{435:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[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("Flutter")]),t._v(" 官方提供了 "),s("code",[t._v("ScrollController")]),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("jumpTo")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("double value"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\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 class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("animateTo")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n double offset"),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 required "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Duration")]),t._v(" duration"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n required "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Curve")]),t._v(" curve"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("p",[t._v("痛点二:")]),t._v(" "),s("p",[t._v("为了解决痛点一,业内有很多第三方库实现了这个功能,其中最知名的莫过于谷歌的 "),s("a",{attrs:{href:"https://pub.dev/packages/scrollable_positioned_list",target:"_blank",rel:"noopener noreferrer"}},[t._v("scrollable_positioned_list"),s("OutboundLink")],1)]),t._v(" "),s("p",[t._v("而这些库都有一些相同的问题:")]),t._v(" "),s("ul",[s("li",[t._v("侵入性强,必须使用他们提供的 "),s("code",[t._v("Widget")]),t._v(" 来构建列表视图")]),t._v(" "),s("li",[t._v("不支持 "),s("code",[t._v("GridView")])])]),t._v(" "),s("p",[t._v("对此,我决定自己编写一个库("),s("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("flutter_scrollview_observer"),s("OutboundLink")],1),t._v(")来实现这个功能,以及解决以上的问题。")]),t._v(" "),s("h2",{attrs:{id:"二、优点"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#二、优点"}},[t._v("#")]),t._v(" 二、优点")]),t._v(" "),s("ol",[s("li",[t._v("侵入性低,不会限制你的列表视图实现,只要求将列表视图做为 "),s("code",[t._v("ViewObserver")]),t._v(" 的 "),s("code",[t._v("child")]),t._v(",如果不想用了,直接移除掉 "),s("code",[t._v("ViewObserver")]),t._v(" 即可。")]),t._v(" "),s("li",[t._v("基于"),s("code",[t._v("【不会限制你的列表视图实现】")]),t._v("这一点, "),s("code",[t._v("flutter_scrollview_observer")]),t._v(" 支持所有的列表"),s("code",[t._v("Widget")]),t._v(",如:"),s("code",[t._v("ListView")]),t._v("、"),s("code",[t._v("GridView")]),t._v(",甚至 "),s("code",[t._v("CustomSrollView")])]),t._v(" "),s("li",[t._v("防抖动,如果在滚动到指定下标时,列表尾部的可滚动区域已不足以支撑滚动到相应位置,则会自动滚动到最底部,避免回弹的问题")])]),t._v(" "),s("p",[t._v("下面正式介绍一下这个库的使用")]),t._v(" "),s("h2",{attrs:{id:"三、使用"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#三、使用"}},[t._v("#")]),t._v(" 三、使用")]),t._v(" "),s("h3",{attrs:{id:"_1、listview"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、listview"}},[t._v("#")]),t._v(" 1、"),s("code",[t._v("ListView")])]),t._v(" "),s("p",[t._v("正常创建和使用 "),s("code",[t._v("ScrollController")]),t._v(" 实例")]),t._v(" "),s("blockquote",[s("p",[t._v("这里你可以使用 "),s("code",[t._v("ListView")]),t._v(" 或者 "),s("code",[t._v("CustomSrollView")]),t._v(" 的 "),s("code",[t._v("SliverList")])])]),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 class-name"}},[t._v("ScrollController")]),t._v(" scrollController "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ScrollController")]),s("span",{pre:!0,attrs:{class:"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 class-name"}},[t._v("ListView")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildListView")]),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 class-name"}},[t._v("ListView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("separated")]),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(" scrollController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("}")]),t._v("\n")])])]),s("p",[t._v("创建 "),s("code",[t._v("ListObserverController")]),t._v(" 实例并将其传递给 "),s("code",[t._v("ListViewObserver")])]),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 class-name"}},[t._v("ListObserverController")]),t._v(" observerController "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ListObserverController")]),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(" scrollController"),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 class-name"}},[t._v("ListViewObserver")]),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(" observerController"),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("_buildListView")]),s("span",{pre:!0,attrs:{class:"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")])])]),s("blockquote",[s("p",[t._v("注意:创建 "),s("code",[t._v("ListObserverController")]),t._v(" 实例时需要传入列表的 "),s("code",[t._v("ScrollController")]),t._v(" 实例。\n这一步很重要,因为 "),s("code",[t._v("flutter_scrollview_observer")]),t._v(" 内部实现原理是基于官方 "),s("code",[t._v("ScrollController")]),t._v(" 提供的 "),s("code",[t._v("jumpTo")]),t._v(" 和 "),s("code",[t._v("animateTo")]),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 comment"}},[t._v("// 无动画滚动至下标位置")]),t._v("\nobserverController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("jumpTo")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("index"),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\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 动画滚动至下标位置")]),t._v("\nobserverController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("animateTo")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n index"),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 duration"),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 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("\n curve"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Curves")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ease"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202208120700435.gif",alt:""}})]),t._v(" "),s("h3",{attrs:{id:"_2、gridview"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、gridview"}},[t._v("#")]),t._v(" 2、"),s("code",[t._v("GridView")])]),t._v(" "),s("blockquote",[s("p",[t._v("由于内容与 "),s("code",[t._v("ListView")]),t._v(" 基本一致,所以这里就折叠了,需要的小伙伴可点击"),s("code",[t._v("【展开查看】")])])]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202208120700807.gif",alt:""}})]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202208120700476.gif",alt:""}})]),t._v(" "),s("details",[s("summary",[t._v("展开查看")]),t._v(" "),s("p",[t._v("正常创建和使用 "),s("code",[t._v("ScrollController")]),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 class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildGridView")]),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 class-name"}},[t._v("GridView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("\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 controller"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" scrollController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("}")]),t._v("\n")])])]),s("p",[t._v("创建 "),s("code",[t._v("GridObserverController")]),t._v(" 实例并将其传递给 "),s("code",[t._v("GridViewObserver")])]),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 class-name"}},[t._v("GridObserverController")]),t._v(" observerController "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("GridObserverController")]),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(" scrollController"),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 class-name"}},[t._v("GridViewObserver")]),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(" observerController"),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("_buildGridView")]),s("span",{pre:!0,attrs:{class:"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("现在即可滚动到指定下标位置了")]),t._v(" "),s("div",{staticClass:"language-dart extra-class"},[s("pre",{pre:!0,attrs:{class:"language-dart"}},[s("code",[t._v("observerController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("jumpTo")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("40")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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\nobserverController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("animateTo")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("40")]),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 keyword"}},[t._v("const")]),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("\n curve"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Curves")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ease"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("h3",{attrs:{id:"_3、customsrollview"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_3、customsrollview"}},[t._v("#")]),t._v(" 3、"),s("code",[t._v("CustomSrollView")])]),t._v(" "),s("blockquote",[s("p",[t._v("支持 "),s("code",[t._v("SliverList")]),t._v(" 和 "),s("code",[t._v("SliverGrid")]),t._v(" 混合使用的情况!")])]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202208120659803.gif",alt:""}})]),t._v(" "),s("p",[t._v("如上图所示,"),s("code",[t._v("CustomSrollView")]),t._v(" 的 "),s("code",[t._v("slivers")]),t._v(" 中包含有 "),s("code",[t._v("SliverList")]),t._v(" 和 "),s("code",[t._v("SliverGrid")])]),t._v(" "),s("ul",[s("li",[t._v("点击右下角第一个按钮可滚动到 "),s("code",[t._v("SliverList")]),t._v(" 的第 "),s("code",[t._v("29")]),t._v(" 行")]),t._v(" "),s("li",[t._v("点击第二个按钮可滚动到 "),s("code",[t._v("SliverGrid")]),t._v(" 的第 "),s("code",[t._v("10")]),t._v(" 的子部件所在行")])]),t._v(" "),s("details",[s("summary",[t._v("展开查看")]),t._v(" "),s("p",[t._v("正常创建和使用 "),s("code",[t._v("ScrollController")]),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 class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildScrollView")]),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 class-name"}},[t._v("CustomScrollView")]),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(" scrollController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// scrollDirection: Axis.horizontal,")]),t._v("\n slivers"),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("_buildSliverListView")]),s("span",{pre:!0,attrs:{class:"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("_buildSliverGridView")]),s("span",{pre:!0,attrs:{class:"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("p",[t._v("正常创建你的 "),s("code",[t._v("SliverList")]),t._v(" 和 "),s("code",[t._v("SliverGrid")]),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 class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildSliverListView")]),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 class-name"}},[t._v("SliverList")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n delegate"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverChildBuilderDelegate")]),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("ctx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" index"),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 _sliverListCtx "),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 operator"}},[t._v("=")]),t._v(" ctx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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(".")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildSliverGridView")]),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 class-name"}},[t._v("SliverGrid")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 delegate"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverChildBuilderDelegate")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildContext")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" int index"),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 _sliverGridCtx "),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 operator"}},[t._v("=")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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(".")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("创建 "),s("code",[t._v("SliverObserverController")]),t._v(" 实例并将其传递给 "),s("code",[t._v("SliverViewObserver")])]),t._v(" "),s("p",[t._v("注意:在 "),s("code",[t._v("sliverListContexts")]),t._v(" 里返回刚才记录的两个 "),s("code",[t._v("Sliver")]),t._v(" 的 "),s("code",[t._v("BuildContext")])]),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 class-name"}},[t._v("SliverObserverController")]),t._v(" observerController "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverObserverController")]),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(" scrollController"),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 class-name"}},[t._v("SliverViewObserver")]),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(" observerController"),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("_buildScrollView")]),s("span",{pre:!0,attrs:{class:"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 sliverListContexts"),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 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("_sliverListCtx "),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(" _sliverListCtx"),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 keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("_sliverGridCtx "),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(" _sliverGridCtx"),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(";")]),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("p",[t._v("现在即可滚动到指定下标位置了")]),t._v(" "),s("div",{staticClass:"language-dart extra-class"},[s("pre",{pre:!0,attrs:{class:"language-dart"}},[s("code",[t._v("observerController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("animateTo")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n sliverContext"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" _sliverListCtx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("29")]),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 keyword"}},[t._v("const")]),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("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 curve"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Curves")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("easeInOut"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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\nobserverController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("animateTo")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n sliverContext"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" _sliverGridCtx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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 duration"),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 class-name"}},[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 curve"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Curves")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("easeInOut"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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:"四、说明"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#四、说明"}},[t._v("#")]),t._v(" 四、说明")]),t._v(" "),s("h3",{attrs:{id:"_1、viewobserver-的选择"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、viewobserver-的选择"}},[t._v("#")]),t._v(" 1、"),s("code",[t._v("ViewObserver")]),t._v(" 的选择")]),t._v(" "),s("p",[t._v("建议使用相应的 "),s("code",[t._v("ViewObserver")]),t._v(":")]),t._v(" "),s("ul",[s("li",[t._v("如果是 "),s("code",[t._v("ListView")]),t._v(" 或者纯 "),s("code",[t._v("SliverList")]),t._v(",建议使用 "),s("code",[t._v("ListViewObserver")])]),t._v(" "),s("li",[t._v("如果是 "),s("code",[t._v("GridView")]),t._v(" 或者纯 "),s("code",[t._v("SliverGrid")]),t._v(",建议使用 "),s("code",[t._v("GridViewObserver")])]),t._v(" "),s("li",[t._v("如果是 "),s("code",[t._v("SliverList")]),t._v(" 和 "),s("code",[t._v("SliverGrid")]),t._v(",则使用 "),s("code",[t._v("SliverViewObserver")])])]),t._v(" "),s("p",[t._v("这样在 "),s("code",[t._v("onObserve")]),t._v(" 回调中拿到数据模型后不用再对其进行类型判断与转换。")]),t._v(" "),s("h3",{attrs:{id:"_2、isfixedheight"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、isfixedheight"}},[t._v("#")]),t._v(" 2、"),s("code",[t._v("isFixedHeight")])]),t._v(" "),s("p",[t._v("子部件为固定高度的情况下,请设置 "),s("code",[t._v("isFixedHeight")]),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",[t._v("observerController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("jumpTo")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n index"),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 isFixedHeight"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),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\nobserverController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("animateTo")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n index"),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 isFixedHeight"),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("\n duration"),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 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("\n curve"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Curves")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ease"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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:"_3、slivercontext-是否需要传"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_3、slivercontext-是否需要传"}},[t._v("#")]),t._v(" 3、"),s("code",[t._v("sliverContext")]),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("jumpTo")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n required int index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildContext")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" sliverContext"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n bool isFixedHeight "),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("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("animateTo")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n required int index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n required "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Duration")]),t._v(" duration"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n required "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Curve")]),t._v(" curve"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildContext")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" sliverContext"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n bool isFixedHeight "),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("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("如上,"),s("code",[t._v("jumpTo")]),t._v(" 和 "),s("code",[t._v("animateTo")]),t._v(" 方法中都有一个 "),s("code",[t._v("sliverContext")]),t._v(" 参数,这是用来指定哪个 "),s("code",[t._v("sliver")]),t._v(" 进行滚动操作的。")]),t._v(" "),s("p",[t._v("如果不传 "),s("code",[t._v("sliverContext")]),t._v(",则默认会是列表中的第一个 "),s("code",[t._v("sliver")]),t._v(",只有你想让非第一个 "),s("code",[t._v("sliver")]),t._v(" 进行滚动的时候才需要传该值")]),t._v(" "),s("h2",{attrs:{id:"五、可现实的功能"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#五、可现实的功能"}},[t._v("#")]),t._v(" 五、可现实的功能")]),t._v(" "),s("h3",{attrs:{id:"_1、获得当前视窗中的子部件信息"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、获得当前视窗中的子部件信息"}},[t._v("#")]),t._v(" 1、获得当前视窗中的子部件信息")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202205291628046.gif",alt:""}})]),t._v(" "),s("p",[t._v("如上图控制台所示,在滚动的过程中可以实时获取正在显示的子部件的数据。")]),t._v(" "),s("h3",{attrs:{id:"_2、视频列表自动播放"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、视频列表自动播放"}},[t._v("#")]),t._v(" 2、视频列表自动播放")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202205291628769.gif",alt:""}})]),t._v(" "),s("p",[t._v("这种功能很常见,在列表滚动的时候,指定区域内的被命中的视频便会自动进行播放。")]),t._v(" "),s("h3",{attrs:{id:"_3、模块定位"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_3、模块定位"}},[t._v("#")]),t._v(" 3、模块定位")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202208120701261.gif",alt:""}})]),t._v(" "),s("p",[t._v("一般详情页中都会有的功能,在列表视图滚动的时候,顶部定位导航视图跟着更新下标,点击导航视图某个下标则会定位到对应的模块")]),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("star")]),t._v(" 吧")]),t._v(" "),s("p",[t._v("GitHub: "),s("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("flutter_scrollview_observer"),s("OutboundLink")],1)])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/124.e5c4591e.js b/assets/js/124.e5c4591e.js new file mode 100644 index 000000000..4678568cf --- /dev/null +++ b/assets/js/124.e5c4591e.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[124],{436:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h2",{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("1、聊天数据不满一屏时,顶部显示所有聊天数据")]),t._v(" "),s("li",[t._v("2、插入消息时\n"),s("ul",[s("li",[t._v("如果最新消息紧靠列表底部时,则插入消息会使列表向上推")]),t._v(" "),s("li",[t._v("如果不是紧靠列表底部,则固定到当前聊天位置")])])])]),t._v(" "),s("p",[t._v("效果如图所示:")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202210092241966.gif",alt:""}})]),t._v(" "),s("h2",{attrs:{id:"二、原理"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#二、原理"}},[t._v("#")]),t._v(" 二、原理")]),t._v(" "),s("h3",{attrs:{id:"_1、-涉及的方法"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、-涉及的方法"}},[t._v("#")]),t._v(" 1、 涉及的方法")]),t._v(" "),s("p",[s("code",[t._v("ScrollPhysics")]),t._v(" 提供了 "),s("code",[t._v("adjustPositionForNewDimensions")]),t._v(" 方法,用于修正 "),s("code",[t._v("ScrollView")]),t._v(" 在 "),s("code",[t._v("rebuild")]),t._v(" 后的偏移量,方法声明如下")]),t._v(" "),s("div",{staticClass:"language-dart extra-class"},[s("pre",{pre:!0,attrs:{class:"language-dart"}},[s("code",[t._v("double "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("adjustPositionForNewDimensions")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n required "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ScrollMetrics")]),t._v(" oldPosition"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n required "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ScrollMetrics")]),t._v(" newPosition"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n required bool isScrolling"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n required double velocity"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("newPosition")]),t._v(" 参数的 "),s("code",[t._v("pixels")]),t._v(",所以在顶部插入消息时,消息列表就会跟随滚动。")]),t._v(" "),s("p",[t._v("如下图所示,观察蓝色的消息条目,每播入一条消息时,所有消息会自动往上顶,而滚动视图的偏移量其实一直是没有变化的~")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202210092241840.gif",alt:""}})]),t._v(" "),s("blockquote",[s("p",[t._v("注:值得注意的是,如果该方法返回的值与 "),s("code",[t._v("newPosition")]),t._v(" 的 "),s("code",[t._v("pixels")]),t._v(" 不相等时,则会触发视图的重新布局,所以这个操作还是比较昂贵的,应尽量减少返回值的变动。")])]),t._v(" "),s("h3",{attrs:{id:"_2、实现逻辑"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、实现逻辑"}},[t._v("#")]),t._v(" 2、实现逻辑")]),t._v(" "),s("p",[t._v("根据上述内容我们不难推出插入消息时的效果实现原理如下:")]),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("消息紧靠列表底部时,插入消息会使列表向上推")]),t._v(" "),s("td",[t._v("直接返回 "),s("code",[t._v("super")]),t._v(" 的值,即 "),s("code",[t._v("newPosition")]),t._v(" 参数的 "),s("code",[t._v("pixels")])])]),t._v(" "),s("tr",[s("td",[t._v("如果不是紧靠列表底部,则固定到当前聊天位置")]),t._v(" "),s("td",[t._v("返回原本第 "),s("code",[t._v("0")]),t._v(" 条消息的最新偏移量")])])])]),t._v(" "),s("p",[t._v("下面重点说明一下第 "),s("code",[t._v("2")]),t._v(" 点中 "),s("code",[t._v("返回原本第0条消息的最新偏移量")]),t._v(" 的实现逻辑:")]),t._v(" "),s("p",[s("code",[t._v("ListView")]),t._v(" 的本质是 "),s("code",[t._v("RenderSliverList")]),t._v(",通过 "),s("code",[t._v("RenderSliverList")]),t._v(" 的 "),s("code",[t._v("firstChild")]),t._v(" 属性拿到当前列表中渲染的首个 "),s("code",[t._v("item")]),t._v("。")]),t._v(" "),s("p",[t._v("如下图,"),s("code",[t._v("firstChild")]),t._v(" 是下标为 "),s("code",[t._v("10")]),t._v(" 的 "),s("code",[t._v("item")]),t._v(",这个 "),s("code",[t._v("item")]),t._v(" 与预渲染区域 "),s("code",[t._v("cacheExtent")]),t._v(" 相关,如果将其设置为 "),s("code",[t._v("0")]),t._v(",则 "),s("code",[t._v("firstChild")]),t._v(" 的下标将会是 "),s("code",[t._v("12")]),t._v(",这个相信不难理解。")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202210092242388.png",alt:""}})]),t._v(" "),s("p",[t._v("所以,我们只需要在插入消息时,记录第 "),s("code",[t._v("0")]),t._v(" 条消息的偏移量,当列表视图 "),s("code",[t._v("rebuild")]),t._v(" 后,"),s("code",[t._v("adjustPositionForNewDimensions")]),t._v(" 方法会被调用,此时取出第 "),s("code",[t._v("1")]),t._v(" 条消息的偏移量,两者的差值加上 "),s("code",[t._v("super")]),t._v(" 的值即为目标修正偏移量。")]),t._v(" "),s("p",[t._v("至于 "),s("code",[t._v("聊天数据不满一屏时,顶部显示所有聊天数据")]),t._v(" 这个效果只是在切换 "),s("code",[t._v("shrinkWrap")]),t._v(" 而已,比较简单就不在此展开讲了。")]),t._v(" "),s("p",[t._v("我已将上述逻辑进行了封装,集成于 "),s("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_scrollview_observer#3chat-observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("flutter_scrollview_observer"),s("OutboundLink")],1),t._v(",接下来我们就来看看如何使用。")]),t._v(" "),s("h2",{attrs:{id:"三、使用"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#三、使用"}},[t._v("#")]),t._v(" 三、使用")]),t._v(" "),s("p",[t._v("现在只需三个步骤即可快速实现聊天会话列表的效果")]),t._v(" "),s("p",[t._v("步骤一:初始化必要的 "),s("code",[t._v("ListObserverController")]),t._v(" 和 "),s("code",[t._v("ChatScrollObserver")])]),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("/// 初始化 ListObserverController")]),t._v("\nobserverController "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ListObserverController")]),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(" scrollController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("cacheJumpIndexOffset "),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\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 初始化 ChatScrollObserver")]),t._v("\nchatObserver "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatScrollObserver")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("observerController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("toRebuildScrollViewCallback "),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(" "),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("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("{")]),s("span",{pre:!0,attrs:{class:"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("code",[t._v("ListView")]),t._v(" 并使用 "),s("code",[t._v("ListViewObserver")]),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 class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildListView")]),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 class-name"}},[t._v("Widget")]),t._v(" resultWidget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ListView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("\n physics"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatObserverClampinScrollPhysics")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("observer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" chatObserver"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n shrinkWrap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" chatObserver"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isShrinkWrap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n reverse"),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("\n controller"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" scrollController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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\n resultWidget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ListViewObserver")]),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(" observerController"),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(" resultWidget"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("return")]),t._v(" resultWidget"),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("ChatScrollObserver")]),t._v(" 的 "),s("code",[t._v("standby")]),t._v(" 方法")]),t._v(" "),s("div",{staticClass:"language-dart extra-class"},[s("pre",{pre:!0,attrs:{class:"language-dart"}},[s("code",[t._v("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 chatObserver"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("standby")]),s("span",{pre:!0,attrs:{class:"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("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 chatModels"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("insert")]),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(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatDataHelper")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("createChatModel")]),s("span",{pre:!0,attrs:{class:"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("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("\nonRemove"),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 chatObserver"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("standby")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("isRemove"),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(")")]),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 chatModels"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("removeAt")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("code",[t._v("setState")]),t._v(" 都可以换成对列表视图进行局部刷新的代码")]),t._v(" "),s("h2",{attrs:{id:"四、最后"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#四、最后"}},[t._v("#")]),t._v(" 四、最后")]),t._v(" "),s("p",[t._v("GitHub地址: "),s("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("flutter_scrollview_observer"),s("OutboundLink")],1)]),t._v(" "),s("p",[t._v("该库还实现了其它十分实用的功能,相关功能有对应的文章进行叙述")]),t._v(" "),s("p",[t._v("相关文章链接:")]),t._v(" "),s("ul",[s("li",[s("RouterLink",{attrs:{to:"/pages/beb840/"}},[t._v("Flutter - 列表滚动定位超强辅助库,墙裂推荐!🔥")])],1),t._v(" "),s("li",[s("RouterLink",{attrs:{to:"/pages/0422cb/"}},[t._v("Flutter - 获取ListView当前正在显示的Widget信息")])],1)]),t._v(" "),s("p",[t._v("如果这个库对你很有帮助,请不吝给个 "),s("code",[t._v("star")]),t._v(" 吧")])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/125.aa402bfb.js b/assets/js/125.aa402bfb.js new file mode 100644 index 000000000..7b3f7ed9e --- /dev/null +++ b/assets/js/125.aa402bfb.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[125],{437:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h2",{attrs:{id:"一、问题"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#一、问题"}},[t._v("#")]),t._v(" 一、问题")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202211132206046.gif",alt:""}})]),t._v(" "),s("p",[t._v("可以看出一些问题:")]),t._v(" "),s("ul",[s("li",[t._v("在关闭键盘的情况下,追加表情内容导致换行时,输入框并不会跟随滚动,而键盘输入文字却可以")]),t._v(" "),s("li",[t._v("滚动条在键盘出现时正常,关闭键盘时滚动条无法滑到底部")])]),t._v(" "),s("h2",{attrs:{id:"二、示例代码"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#二、示例代码"}},[t._v("#")]),t._v(" 二、示例代码")]),t._v(" "),s("p",[s("code",[t._v("TextField")]),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("_buildTextField")]),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 class-name"}},[t._v("Scrollbar")]),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(" scrollController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n isAlwaysShown"),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("\n child"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextField")]),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(" textFieldKey"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n autofocus"),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("\n decoration"),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 class-name"}},[t._v("InputDecoration")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n border"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("InputBorder")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("none"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n isCollapsed"),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("\n "),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(" editingController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n scrollController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" scrollController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n keyboardType"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextInputType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("multiline"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n maxLines"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("8")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n minLines"),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 showCursor"),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("\n readOnly"),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("\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("更新输入框内容的逻辑")]),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(" result "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" editingController"),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("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 新增的内容")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" content "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("fetchInsertContent")]),s("span",{pre:!0,attrs:{class:"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("\nint selectionBefore "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" editingController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("selection"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("start"),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("selectionBefore "),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(" selectionBefore "),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"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 更新内容后,光标的位置")]),t._v("\nint selectionAfter "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" selectionBefore "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" content"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("length"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\nresult "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),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-literal"}},[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("insert")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("selectionBefore"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" content"),s("span",{pre:!0,attrs:{class:"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("join")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[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("\neditingController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("text "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" result"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 设置光标")]),t._v("\neditingController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("selection "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextSelection")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("fromPosition")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextPosition")]),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(" selectionAfter"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("h2",{attrs:{id:"三、探索与解决"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#三、探索与解决"}},[t._v("#")]),t._v(" 三、探索与解决")]),t._v(" "),s("h3",{attrs:{id:"_1、输入框"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、输入框"}},[t._v("#")]),t._v(" 1、输入框")]),t._v(" "),s("p",[t._v("当键盘输入内容时会自动滚动到底部,既然跟滚动相关,第一时间就会想到 "),s("code",[t._v("ScrollController")]),t._v(" 的 "),s("code",[t._v("animateTo")]),t._v(" 和 "),s("code",[t._v("jumpTo")]),t._v(" 这两个方法,打个断点后,用键盘输入文字看看,发现果然走了断点")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202211132206601.png",alt:""}})]),t._v(" "),s("p",[t._v("从调用栈里可以看到,"),s("code",[t._v("animateTo")]),t._v(" 方法是在 "),s("code",[t._v("EditableTextState")]),t._v(" 的 "),s("code",[t._v("_scheduleShowCaretOnScreen")]),t._v(" 方法里调用的,该方法正是用于处理在键盘输入文字后,使 "),s("code",[t._v("TextField")]),t._v(" 的内容自动滚动到适当的位置,保持可见。该方法在每次输入文字时都会调用,其内部有相关的滚动偏移计算逻辑。")]),t._v(" "),s("p",[t._v("那好了,我们现在主要就是看如何调用它,因为其是私有方法,所以需要找其它可帮我们间接调用 "),s("code",[t._v("_scheduleShowCaretOnScreen")]),t._v(" 方法的公开方法。")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202211132206146.png",alt:""}})]),t._v(" "),s("p",[t._v("查看调用 "),s("code",[t._v("_scheduleShowCaretOnScreen")]),t._v(" 的方法有哪些后,这里我找到了 "),s("code",[t._v("userUpdateTextEditingValue")]),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("TextEditingValue")]),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 class-name"}},[t._v("TextEditingValue")]),s("span",{pre:!0,attrs:{class:"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("text "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[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 keyword"}},[t._v("this")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("selection "),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 class-name"}},[t._v("TextSelection")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("collapsed")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("offset"),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 keyword"}},[t._v("this")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("composing "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextRange")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("empty"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("table",[s("thead",[s("tr",[s("th",[t._v("属性")]),t._v(" "),s("th",[t._v("描述")])])]),t._v(" "),s("tbody",[s("tr",[s("td",[s("code",[t._v("text")])]),t._v(" "),s("td",[t._v("当前文本")])]),t._v(" "),s("tr",[s("td",[s("code",[t._v("selection")])]),t._v(" "),s("td",[t._v("当前选中的文本下标范围")])]),t._v(" "),s("tr",[s("td",[s("code",[t._v("composing")])]),t._v(" "),s("td",[t._v("待组合内容的下标范围,在中文输入法中很常见,如下图中的 "),s("code",[t._v("lin xun feng")])])])])]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202211132207695.png",alt:""}})]),t._v(" "),s("p",[s("code",[t._v("SelectionChangedCause")]),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("/// Indicates what triggered the change in selected text (including changes to")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// the cursor location).")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SelectionChangedCause")]),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("/// The user tapped on the text and that caused the selection (or the location")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// of the cursor) to change.")]),t._v("\n tap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// The user tapped twice in quick succession on the text and that caused")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// the selection (or the location of the cursor) to change.")]),t._v("\n doubleTap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// The user long-pressed the text and that caused the selection (or the")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// location of the cursor) to change.")]),t._v("\n longPress"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// The user force-pressed the text and that caused the selection (or the")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// location of the cursor) to change.")]),t._v("\n forcePress"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// The user used the keyboard to change the selection or the location of the")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// cursor.")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("///")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// Keyboard-triggered selection changes may be caused by the IME as well as")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// by accessibility tools (e.g. TalkBack on Android).")]),t._v("\n keyboard"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// The user used the selection toolbar to change the selection or the")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// location of the cursor.")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("///")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// An example is when the user taps on select all in the tool bar.")]),t._v("\n toolbar"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// The user used the mouse to change the selection by dragging over a piece")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// of text.")]),t._v("\n drag"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// The user used iPadOS 14+ Scribble to change the selection.")]),t._v("\n scribble"),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("TextEditingController")]),t._v(" 对输入框内容进行修改的方式,传 "),s("code",[t._v("null")]),t._v(" 即可。")]),t._v(" "),s("p",[t._v("需要调用的方式已经确定,接下来就是要获取 "),s("code",[t._v("EditableTextState")]),t._v(",我们先来看一下 "),s("code",[t._v("TextField")]),t._v(" 的源码,因其为 "),s("code",[t._v("StatefulWidget")]),t._v(",所以需要找到对应的 "),s("code",[t._v("State")]),t._v(", 即 "),s("code",[t._v("_TextFieldState")])]),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(" _TextFieldState "),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 generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextField")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("RestorationMixin")]),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("TextSelectionGestureDetectorBuilderDelegate")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AutofillClient")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@override")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("GlobalKey")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("EditableTextState")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" editableTextKey "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("GlobalKey")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("EditableTextState")]),s("span",{pre:!0,attrs:{class:"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\n "),s("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@override")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),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 class-name"}},[t._v("BuildContext")]),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 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 class-name"}},[t._v("Widget")]),t._v(" child "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("RepaintBoundary")]),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 class-name"}},[t._v("UnmanagedRestorationScope")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n bucket"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" bucket"),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 class-name"}},[t._v("EditableText")]),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(" editableTextKey"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n readOnly"),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("readOnly "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("||")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("_isEnabled"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("可以看到,最终 "),s("code",[t._v("build")]),t._v(" 方法返回的是嵌套了 "),s("code",[t._v("EditableText")]),t._v(" 的 "),s("code",[t._v("Widget")]),t._v(",我们只要获取到该 "),s("code",[t._v("EditableText")]),t._v(" 的 "),s("code",[t._v("State")]),t._v("(即 "),s("code",[t._v("EditableTextState")]),t._v(") 就可以调用 "),s("code",[t._v("userUpdateTextEditingValue")]),t._v(" 方法。")]),t._v(" "),s("p",[t._v("现在比较棘手的是,"),s("code",[t._v("TextFieldState")]),t._v(" 是私有的,无法直接访问到,但是我们可以换种方式,直接遍历 "),s("code",[t._v("Element")]),t._v(" 从而获取到 "),s("code",[t._v("EditableTextState")])]),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("visitor")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Element")]),t._v(" element"),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("element"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("widget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("is")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("EditableText")]),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(" editableText "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" element"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("widget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("as")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("EditableText")]),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(" editableTextState "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("editableText"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("key "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("as")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("GlobalKey")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("EditableTextState")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("currentState"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n \n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 找到 EditableTextState,调用 userUpdateTextEditingValue 方法")]),t._v("\n editableTextState"),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("userUpdateTextEditingValue")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextEditingValue")]),s("span",{pre:!0,attrs:{class:"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(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),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(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),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 element"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("visitChildren")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("visitor"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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\ntextFieldKey"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("currentContext"),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("visitChildElements")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("visitor"),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("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202211132207166.gif",alt:""}})]),t._v(" "),s("p",[t._v("注:")]),t._v(" "),s("ol",[s("li",[t._v("这里大家可以自行对遍历的方法和 "),s("code",[t._v("userUpdateTextEditingValue")]),t._v(" 的调用做下调整,毕竟每次变更输入框内容就遍历不好~")]),t._v(" "),s("li",[t._v("设置光标位置的代码不需要了,"),s("code",[t._v("userUpdateTextEditingValue")]),t._v(" 方法内部会做这个事情")])]),t._v(" "),s("h3",{attrs:{id:"_2、滚动条"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、滚动条"}},[t._v("#")]),t._v(" 2、滚动条")]),t._v(" "),s("p",[t._v("只有 "),s("code",[t._v("readOnly")]),t._v(" 为 "),s("code",[t._v("true")]),t._v(" 时才会出现底部间距,对比 "),s("code",[t._v("Scrollbar")]),t._v(" 的 "),s("code",[t._v("updateScrollbarPainter")]),t._v(" 方法中的 "),s("code",[t._v("padding")]),t._v(" 发现端倪")]),t._v(" "),s("p",[s("code",[t._v("readOnly: true")]),t._v(" 的情况:")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202211132208833.png",alt:"readOnly: true"}})]),t._v(" "),s("p",[s("code",[t._v("readOnly: false")]),t._v(" 的情况:")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202211132209608.png",alt:"readOnly: false"}})]),t._v(" "),s("p",[t._v("可以看到 "),s("code",[t._v("readOnly: true")]),t._v(" 的情况下 "),s("code",[t._v("padding")]),t._v(" 的 "),s("code",[t._v("bottom")]),t._v(" 为 "),s("code",[t._v("34.0")]),t._v(",做 "),s("code",[t._v("iOS")]),t._v(" 开发的小伙伴看到这个数值一定会觉得很熟悉,没错,就是底部安全区域的高度,而当键盘出现的时候,"),s("code",[t._v("Scrollbar")]),t._v(" 是不可能接触到底部安全区域的,所以 "),s("code",[t._v("padding")]),t._v(" 的 "),s("code",[t._v("bottom")]),t._v(" 为 "),s("code",[t._v("0")]),t._v("。")]),t._v(" "),s("p",[t._v("这个时候我们只需要想办法让 "),s("code",[t._v("Scrollbar")]),t._v(" 获取到的 "),s("code",[t._v("padding")]),t._v(" 的 "),s("code",[t._v("bottom")]),t._v(" 为 "),s("code",[t._v("0")]),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 class-name"}},[t._v("MediaQuery")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("removePadding")]),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(" context"),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("_buildTextField")]),s("span",{pre:!0,attrs:{class:"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 removeTop"),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("\n removeBottom"),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(" "),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",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202211132209013.gif",alt:""}})]),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("GitHub")]),t._v(": "),s("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_demo/tree/main/lib/features/chat_text_field",target:"_blank",rel:"noopener noreferrer"}},[t._v("LinXunFeng/flutter_demo"),s("OutboundLink")],1)])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/126.e39ce489.js b/assets/js/126.e39ce489.js new file mode 100644 index 000000000..ba7782d73 --- /dev/null +++ b/assets/js/126.e39ce489.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[126],{438:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h2",{attrs:{id:"一、背景"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#一、背景"}},[t._v("#")]),t._v(" 一、背景")]),t._v(" "),s("p",[s("code",[t._v("Flutter")]),t._v(" 项目在 "),s("code",[t._v("iPhone")]),t._v(" 真机上运行会白屏,"),s("code",[t._v("VSCode")]),t._v(" 右下角一直卡在 "),s("code",[t._v("Installing and launching...")]),t._v(","),s("code",[t._v("AndroidStudio")]),t._v(" 亦是如此。")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202302111246252.png",alt:""}})]),t._v(" "),s("p",[t._v("其它人的电脑又是正常的,遂着手进行问题的定位,并在解决问题后向 "),s("code",[t._v("Flutter")]),t._v(" 官方提交了我的第一份 "),s("code",[t._v("PR")]),t._v("。")]),t._v(" "),s("p",[s("code",[t._v("PR")]),t._v(" 链接:"),s("a",{attrs:{href:"https://github.com/flutter/flutter/pull/119443",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/flutter/flutter/pull/119443"),s("OutboundLink")],1)]),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("flutter run -d xxxx -v")]),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 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 punctuation"}},[t._v("[")]),t._v(" +187 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("LinXunFeng"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$command")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("source")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-s")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'/tmp/3EB9B732-D866-46D2-88CB-513C2CF7455E/fruitstrap-lldb-prep-cmds-00008120_0019784E2690C01E'")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Executing commands "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'/tmp/3EB9B732-D866-46D2-88CB-513C2CF7455E/fruitstrap-lldb-prep-cmds-00008120_0019784E2690C01E'")]),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v("\n"),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 punctuation"}},[t._v("[")]),t._v("LinXunFeng"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("$ platform "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),t._v(" remote-"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'ios'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--sysroot")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'/Users/lxf/Library/Developer/Xcode/iOS\nDeviceSupport/16.1.1 (20B101) arm64e/Symbols'")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Platform: remote-ios\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Connected: no\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" SDK Path: "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Library/Developer/Xcode/iOS DeviceSupport/16.1.1 (20B101) arm64e/Symbols"')]),t._v("\n"),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 punctuation"}},[t._v("[")]),t._v("LinXunFeng"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("$ target create\n"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/gitHub/flutter_scrollview_observer/example/build/ios/iphoneos/Runner.app"')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("+3544 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Current executable "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" to\n"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'/Users/lxf/Desktop/gitHub/flutter_scrollview_observer/example/build/ios/iphoneos/Runner.app'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("arm64"),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 punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("LinXunFeng"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("$ script\n"),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("fruitstrap_device_app")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/private/var/containers/Bundle/Application/1915BC3A-57D0-41A5-8121-1769CF3F6777/Runner.app"')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +124 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("LinXunFeng"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("$ script "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("fruitstrap_connect_url")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"connect://127.0.0.1:65222"')]),t._v("\n"),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 punctuation"}},[t._v("[")]),t._v("LinXunFeng"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("$ script "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("fruitstrap_output_path")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),t._v("\n"),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 punctuation"}},[t._v("[")]),t._v("LinXunFeng"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("$ script "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("fruitstrap_error_path")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),t._v("\n"),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 punctuation"}},[t._v("[")]),t._v("LinXunFeng"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("$ target modules search-paths "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),t._v(" /usr "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Library/Developer/Xcode/iOS\nDeviceSupport/16.1.1 (20B101) arm64e/Symbols/usr"')]),t._v(" /System "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Library/Developer/Xcode/iOS\nDeviceSupport/16.1.1 (20B101) arm64e/Symbols/System"')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/private/var/containers/Bundle/Application/1915BC3A-57D0-41A5-8121-1769CF3F6777"')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/gitHub/flutter_scrollview_observer/example/build/ios/iphoneos"')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/var/containers/Bundle/Application/1915BC3A-57D0-41A5-8121-1769CF3F6777"')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/gitHub/flutter_scrollview_observer/example/build/ios/iphoneos"')]),t._v(" /Developer\n"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Library/Developer/Xcode/iOS DeviceSupport/16.1.1 (20B101) arm64e/Symbols/Developer"')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +15 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("LinXunFeng"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("$ "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("command")]),t._v(" script "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("import")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/tmp/3EB9B732-D866-46D2-88CB-513C2CF7455E/fruitstrap_00008120_0019784E2690C01E.py"')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +3 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("LinXunFeng"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("$ "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("command")]),t._v(" script "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" fruitstrap_00008120_0019784E2690C01E.connect_command connect\n"),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 punctuation"}},[t._v("[")]),t._v("LinXunFeng"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("$ "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("command")]),t._v(" script "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-s")]),t._v(" asynchronous "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" fruitstrap_00008120_0019784E2690C01E.run_command\nrun\n"),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 punctuation"}},[t._v("[")]),t._v("LinXunFeng"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("$ "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("command")]),t._v(" script "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-s")]),t._v(" asynchronous "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v("\nfruitstrap_00008120_0019784E2690C01E.autoexit_command autoexit\n"),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 punctuation"}},[t._v("[")]),t._v("LinXunFeng"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("$ "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("command")]),t._v(" script "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-s")]),t._v(" asynchronous "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v("\nfruitstrap_00008120_0019784E2690C01E.safequit_command safequit\n"),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 punctuation"}},[t._v("[")]),t._v("LinXunFeng"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("$ connect\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +29 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("LinXunFeng"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("$ run\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +109 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" success\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("+4740 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("LinXunFeng"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$2023")]),t._v("-02-02 08:57:41.425078+0800 Runner"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5278")]),t._v(":1703137"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" flutter: The Dart VM "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("service")]),t._v(" is\nlistening on http://127.0.0.1:61063/\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 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 punctuation"}},[t._v("[")]),t._v(" +359 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("lldb"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("command")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("source")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-s")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'/tmp/70CB9E5A-3BA1-4299-8F38-BE5135178035/fruitstrap-lldb-prep-cmds-00008120_0019784E2690C01E'")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Executing commands "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'/tmp/70CB9E5A-3BA1-4299-8F38-BE5135178035/fruitstrap-lldb-prep-cmds-00008120_0019784E2690C01E'")]),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v("\n"),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 punctuation"}},[t._v("(")]),t._v("lldb"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" platform "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),t._v(" remote-"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'ios'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--sysroot")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'/Users/lxf/Library/Developer/Xcode/iOS\nDeviceSupport/16.1.1 (20B101) arm64e/Symbols'")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Platform: remote-ios\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Connected: no\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" SDK Path: "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Library/Developer/Xcode/iOS DeviceSupport/16.1.1 (20B101) arm64e/Symbols"')]),t._v("\n"),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 punctuation"}},[t._v("(")]),t._v("lldb"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" target create\n"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/gitHub/flutter_scrollview_observer/example/build/ios/iphoneos/Runner.app"')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("+3984 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Current executable "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" to\n"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'/Users/lxf/Desktop/gitHub/flutter_scrollview_observer/example/build/ios/iphoneos/Runner.app'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("arm64"),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 punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("lldb"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" script\n"),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("fruitstrap_device_app")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/private/var/containers/Bundle/Application/ABD16CE1-675A-44C3-BE9C-18EC156B6499/Runner.app"')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +136 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("lldb"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" script "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("fruitstrap_connect_url")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"connect://127.0.0.1:65102"')]),t._v("\n"),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 punctuation"}},[t._v("(")]),t._v("lldb"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" script "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("fruitstrap_output_path")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),t._v("\n"),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 punctuation"}},[t._v("(")]),t._v("lldb"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" script "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("fruitstrap_error_path")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),t._v("\n"),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 punctuation"}},[t._v("(")]),t._v("lldb"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" target modules search-paths "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),t._v(" /usr "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Library/Developer/Xcode/iOS\nDeviceSupport/16.1.1 (20B101) arm64e/Symbols/usr"')]),t._v(" /System "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Library/Developer/Xcode/iOS\nDeviceSupport/16.1.1 (20B101) arm64e/Symbols/System"')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/private/var/containers/Bundle/Application/ABD16CE1-675A-44C3-BE9C-18EC156B6499"')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/gitHub/flutter_scrollview_observer/example/build/ios/iphoneos"')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/var/containers/Bundle/Application/ABD16CE1-675A-44C3-BE9C-18EC156B6499"')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/gitHub/flutter_scrollview_observer/example/build/ios/iphoneos"')]),t._v(" /Developer\n"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Library/Developer/Xcode/iOS DeviceSupport/16.1.1 (20B101) arm64e/Symbols/Developer"')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +17 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("lldb"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("command")]),t._v(" script "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("import")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/tmp/70CB9E5A-3BA1-4299-8F38-BE5135178035/fruitstrap_00008120_0019784E2690C01E.py"')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +3 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("lldb"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("command")]),t._v(" script "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" fruitstrap_00008120_0019784E2690C01E.connect_command connect\n"),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 punctuation"}},[t._v("(")]),t._v("lldb"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("command")]),t._v(" script "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-s")]),t._v(" asynchronous "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" fruitstrap_00008120_0019784E2690C01E.run_command run\n"),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 punctuation"}},[t._v("(")]),t._v("lldb"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("command")]),t._v(" script "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-s")]),t._v(" asynchronous "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" fruitstrap_00008120_0019784E2690C01E.autoexit_command\nautoexit\n"),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 punctuation"}},[t._v("(")]),t._v("lldb"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("command")]),t._v(" script "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-s")]),t._v(" asynchronous "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" fruitstrap_00008120_0019784E2690C01E.safequit_command\nsafequit\n"),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 punctuation"}},[t._v("(")]),t._v("lldb"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" connect\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +38 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("lldb"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" run\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +118 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" success\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Application launched on the device. Waiting "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" observatory url.\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("+4521 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("lldb"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2023")]),t._v("-02-02 08:55:53.841520+0800 Runner"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5272")]),t._v(":1701483"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Warning: Unable to create restoration "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v("\nprogress marker "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("file")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +45 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Observatory URL on device: http://127.0.0.1:60477/\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +3 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Attempting to forward device port "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("60477")]),t._v(" to "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("host")]),t._v(" port "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("65119")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" executing: /Users/lxf/fvm/versions/3.3.7/bin/cache/artifacts/usbmuxd/iproxy "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("65119")]),t._v(":60477 "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--udid")]),t._v("\n00008120-0019784E2690C01E "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--debug")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +433 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" fopen failed "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" data file: errno "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),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("No such "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("file")]),t._v(" or directory"),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 punctuation"}},[t._v("]")]),t._v(" Errors found"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v(" Invalidating cache"),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(" +13 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" fopen failed "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" data file: errno "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),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("No such "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("file")]),t._v(" or directory"),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 punctuation"}},[t._v("]")]),t._v(" Errors found"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v(" Invalidating cache"),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(" +560 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Forwarded port ForwardedPort HOST:65119 to DEVICE:60477\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Forwarded "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("host")]),t._v(" port "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("65119")]),t._v(" to device port "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("60477")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" Observatory\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Installing and launching"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(". "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("completed "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("19")]),t._v(".1s"),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 punctuation"}},[t._v("]")]),t._v(" Caching compiled dill\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +16 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Connecting to "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("service")]),t._v(" protocol: http://127.0.0.1:65119/\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +92 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Launching a Dart Developer Service "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("DDS"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" instance at http://127.0.0.1:0, connecting to VM "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("service")]),t._v(" at\nhttp://127.0.0.1:65119/.\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +45 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" DDS is listening at http://127.0.0.1:65124/MCCddjLRlQk"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("/.\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +22 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Successfully connected to "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("service")]),t._v(" protocol: http://127.0.0.1:65119/\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +13 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" DevFS: Creating new filesystem on the device "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),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(" +10 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" DevFS: Created new filesystem on the device\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("file:///private/var/mobile/Containers/Data/Application/9066D3F0-F8FB-489D-99BD-FCAF266B118F/tmp/examplePN73Pp/exa\nmple/"),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 punctuation"}},[t._v("]")]),t._v(" Updating assets\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +34 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Syncing files to device lxf的iPhone"),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 punctuation"}},[t._v("]")]),t._v(" Compiling dart to kernel with "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" updated files\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Processing bundle.\n"),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 operator"}},[t._v("<")]),t._v("- recompile package:scrollview_observer_example/main.dart ef8dbbad-3cff-4e06-9043-63987bdc88a2\n"),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 operator"}},[t._v("<")]),t._v("- ef8dbbad-3cff-4e06-9043-63987bdc88a2\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Bundle processing done.\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +30 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Updating files.\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" DevFS: Sync finished\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Syncing files to device lxf的iPhone"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(". "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("completed "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" 33ms"),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 punctuation"}},[t._v("]")]),t._v(" Synced "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(".0MB.\n"),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 operator"}},[t._v("<")]),t._v("- accept\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +7 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Connected to _flutterView/0x10881a420.\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +1 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Flutter run key commands.\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" r Hot reload. 🔥🔥🔥\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" R Hot restart.\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" h List all available interactive commands.\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" d Detach "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("terminate "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"flutter run"')]),t._v(" but leave application running"),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 punctuation"}},[t._v("]")]),t._v(" c Clear the "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("screen")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" q Quit "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("terminate the application on the 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(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" 💪 Running with sound null safety 💪\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" An Observatory debugger and profiler on lxf的iPhone is available at:\nhttp://127.0.0.1:65124/MCCddjLRlQk"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("/\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +29 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" The Flutter DevTools debugger and profiler on lxf的iPhone is available at:\n http://127.0.0.1:9102?uri"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("http://127.0.0.1:65124/MCCddjLRlQk"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("/\n")])])]),s("p",[t._v("在不断谷歌 "),s("code",[t._v("Flutter Installing and launching...")]),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 punctuation"}},[t._v("[")]),t._v(" +29 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("LinXunFeng"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("$ run\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +109 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" success\n\n\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +38 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("lldb"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" run\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" +118 ms"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" success\n")])])]),s("p",[t._v("为什么我会觉得它可疑呢?因为我之前学 "),s("code",[t._v("iOS")]),t._v(" 逆向的时候自定义过 "),s("code",[t._v("lldb")]),t._v(" 提示符,然后差不多在那个时间段后 "),s("code",[t._v("Flutter")]),t._v(" 项目就不再能正常运行,而且从日志中可以看出到这里就不再往下执行了~")]),t._v(" "),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 comment"}},[t._v("# ~/.lldbinit 文件的内容")]),t._v("\nsettings "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" prompt "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"\\[LinXunFeng]$"')]),t._v("\n")])])]),s("p",[t._v("将其注释掉,竟完美解决了这个问题~")]),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("lldb")]),t._v(" 提示符就好了,那说明 "),s("code",[t._v("Flutter")]),t._v(" 内部八成是根据这个固定的输出来做判断是否进入下一步,打开 "),s("code",[t._v("Flutter")]),t._v(" 源码进行全局搜索 "),s("code",[t._v("(lldb) run")]),t._v(",果不其然,在 "),s("code",[t._v("flutter_tools")]),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("// (lldb) run")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// https://github.com/ios-control/ios-deploy/blob/1.11.2-beta.1/src/ios-deploy/ios-deploy.m#L51")]),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("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("RegExp")]),t._v(" _lldbRun "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("RegExp")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("r'\\(lldb\\)\\s*run'")])]),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("a",{attrs:{href:"https://github.com/flutter/flutter/blob/3.1.0/packages/flutter_tools/lib/src/ios/ios_deploy.dart#L285-L287",target:"_blank",rel:"noopener noreferrer"}},[t._v("flutter_tools/ios_deploy.dart#L285-L287"),s("OutboundLink")],1)]),t._v(" "),s("p",[t._v("在文件下搜索 "),s("code",[t._v("_lldbRun")]),t._v(" 定位到 "),s("code",[t._v("launchAndAttach")]),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("/// Launch the app on the device, and attach the debugger.")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("///")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// Returns whether or not the debugger successfully attached.")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("bool"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("launchAndAttach")]),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("// Return when the debugger attaches, or the ios-deploy process exits.")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Completer")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("bool"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" debuggerCompleter "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Completer")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("bool"),s("span",{pre:!0,attrs:{class:"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("try")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n _iosDeployProcess "),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(" _processUtils"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("start")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n _launchCommand"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n environment"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" _iosDeployEnv"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" lastLineFromDebugger"),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("StreamSubscription")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" stdoutSubscription "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" _iosDeployProcess"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("stdout\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("transform"),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("utf8"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("decoder"),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("transform"),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LineSplitter")]),s("span",{pre:!0,attrs:{class:"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 function"}},[t._v("listen")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" line"),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("_monitorIOSDeployFailure")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("line"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" _logger"),s("span",{pre:!0,attrs:{class:"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("// (lldb) run")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// success")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 2020-09-15 13:42:25.185474-0700 Runner[477:181141] flutter: The Dart VM service is listening on http://127.0.0.1:57782/")]),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("_lldbRun"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("hasMatch")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("line"),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 _logger"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("printTrace")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("line"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n _debuggerState "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" _IOSDeployDebuggerState"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("launching"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),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('// Next line after "run" must be "success", or the attach failed.')]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v('// Example: "error: process launch failed"')]),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("_debuggerState "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" _IOSDeployDebuggerState"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("launching"),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 _logger"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("printTrace")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("line"),s("span",{pre:!0,attrs:{class:"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(" bool attachSuccess "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" line "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'success'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n _debuggerState "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" attachSuccess "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" _IOSDeployDebuggerState"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("attached "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" _IOSDeployDebuggerState"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("detached"),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("(")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("debuggerCompleter"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isCompleted"),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 debuggerCompleter"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("complete")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("attachSuccess"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 keyword"}},[t._v("return")]),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 punctuation"}},[t._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 "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" debuggerCompleter"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("future"),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("a",{attrs:{href:"https://github.com/flutter/flutter/blob/3.1.0/packages/flutter_tools/lib/src/ios/ios_deploy.dart#L319-L351",target:"_blank",rel:"noopener noreferrer"}},[t._v("flutter_tools/ios_deploy.dart#L319-L351"),s("OutboundLink")],1)]),t._v(" "),s("p",[t._v("见名识义,将项目运行起来后进行附加调试,这段代码的执行流程如下:")]),t._v(" "),s("ol",[s("li",[t._v("调用 "),s("code",[t._v("_processUtils")]),t._v(" 的 "),s("code",[t._v("start")]),t._v(" 方法执行 "),s("code",[t._v("_launchCommand")]),t._v(",然后监听其输出内容。")]),t._v(" "),s("li",[t._v("在监听到 "),s("code",[t._v("(lldb) run")]),t._v(" 后将 "),s("code",[t._v("_debuggerState")]),t._v(" 赋值为 "),s("code",[t._v("_IOSDeployDebuggerState.launching")]),t._v(",以做为判断紧跟其后的 "),s("code",[t._v("success")]),t._v(" 是目标输出内容的依据")]),t._v(" "),s("li",[t._v("接着进行了最关键的一步 "),s("code",[t._v("debuggerCompleter.complete(attachSuccess)")]),t._v(",告诉外部该功能已成功执行")])]),t._v(" "),s("p",[t._v("而我的电脑因为自定义 "),s("code",[t._v("LLDB prompt")]),t._v(" 导致这个小流程中的 "),s("code",[t._v("2")]),t._v(" 和 "),s("code",[t._v("3")]),t._v(" 无法顺利执行~~")]),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("lldb")]),t._v(" 提示符然后将 "),s("code",[t._v("_lldbRun")]),t._v(" 中的 "),s("code",[t._v("(lldb)")]),t._v(" 字符串给替换掉,那如何获取当前的 lldb 提示符?")]),t._v(" "),s("p",[t._v("还记得上面提到的常量 "),s("code",[t._v("_lldbRun")]),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("// (lldb) run")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// https://github.com/ios-control/ios-deploy/blob/1.11.2-beta.1/src/ios-deploy/ios-deploy.m#L51")]),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("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("RegExp")]),t._v(" _lldbRun "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("RegExp")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("r'\\(lldb\\)\\s*run'")])]),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("platform select remote-'ios' --sysroot")]),t._v(",定义如下:")]),t._v(" "),s("div",{staticClass:"language-objectivec extra-class"},[s("pre",{pre:!0,attrs:{class:"language-objectivec"}},[s("code",[s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/*\n * Startup script passed to lldb.\n * To see how xcode interacts with lldb, put this into .lldbinit:\n * log enable -v -f /Users/vargaz/lldb.log lldb all\n * log enable -v -f /Users/vargaz/gdb-remote.log gdb-remote all\n */")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token macro property"}},[s("span",{pre:!0,attrs:{class:"token directive-hash"}},[t._v("#")]),s("span",{pre:!0,attrs:{class:"token directive keyword"}},[t._v("define")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token macro-name"}},[t._v("LLDB_PREP_CMDS")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token expression"}},[s("span",{pre:!0,attrs:{class:"token function"}},[t._v("CFSTR")]),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 expression"}},[t._v("platform select remote"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("ios "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("--")]),t._v("sysroot ")]),s("span",{pre:!0,attrs:{class:"token char"}},[t._v("'{symbols_path}'")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("\\n")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token expression"}},[s("span",{pre:!0,attrs:{class:"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 connect\\n\\\n"'),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("代码摘自:"),s("a",{attrs:{href:"https://github.com/ios-control/ios-deploy/blob/1.11.2-beta.1/src/ios-deploy/ios-deploy.m#L33",target:"_blank",rel:"noopener noreferrer"}},[t._v("ios-control/ios-deploy.m#L33"),s("OutboundLink")],1)]),t._v(" "),s("p",[t._v("那就好办了,这个 "),s("code",[t._v("platform select remote-'ios' --sysroot")]),t._v(" 的输出比 "),s("code",[t._v("run")]),t._v(" 要早,所以只需要监听其被输出后进行字符串截取,就可以取到当前的 "),s("code",[t._v("lldb")]),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 punctuation"}},[t._v("[")]),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("LinXunFeng"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("$ platform "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),t._v(" remote-"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'ios'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--sysroot")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'/Users/lxf/Library/Developer/Xcode/iOS\nDeviceSupport/16.1.1 (20B101) arm64e/Symbols'")]),t._v("\n")])])]),s("p",[t._v("具体代码:")]),t._v(" "),s("p",[t._v("1、定义 "),s("code",[t._v("_lldbPlatformSelect")]),t._v(" 常量")]),t._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // (lldb) run\n")]),s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // https://github.com/ios-control/ios-deploy/blob/1.11.2-beta.1/src/ios-deploy/ios-deploy.m#L51\n")]),s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" static final RegExp _lldbRun = RegExp(r'\\(lldb\\)\\s*run');\n")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // (lldb) platform select remote-'ios' --sysroot\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // https://github.com/ios-control/ios-deploy/blob/1.11.2-beta.1/src/ios-deploy/ios-deploy.m#L33\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(' // This regex is to get the configurable lldb prompt. By default this prompt will be "lldb".\n')]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" static final RegExp _lldbPlatformSelect = RegExp(r\"\\s*platform select remote-'ios' --sysroot\");\n")])])])])]),s("p",[t._v("2、调整 "),s("code",[t._v("launchAndAttach")]),t._v(" 方法")]),t._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[t._v("Future launchAndAttach() async {\n"),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // Return when the debugger attaches, or the ios-deploy process exits.\n")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // (lldb) run\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // https://github.com/ios-control/ios-deploy/blob/1.11.2-beta.1/src/ios-deploy/ios-deploy.m#L51\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" RegExp lldbRun = RegExp(r'\\(lldb\\)\\s*run');\n")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" final Completer debuggerCompleter = Completer();\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" try {\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" _iosDeployProcess = await _processUtils.start(\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" _launchCommand,\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" environment: _iosDeployEnv,\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" );\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" String? lastLineFromDebugger;\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" final StreamSubscription stdoutSubscription = _iosDeployProcess!.stdout\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" .transform(utf8.decoder)\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" .transform(const LineSplitter())\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" .listen((String line) {\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" _monitorIOSDeployFailure(line, _logger);\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // (lldb) platform select remote-'ios' --sysroot\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // Use the configurable custom lldb prompt in the regex. The developer can set this prompt to anything.\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(' // For example `settings set prompt "(mylldb)"` in ~/.lldbinit results in:\n')]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // \"(mylldb) platform select remote-'ios' --sysroot\"\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" if (_lldbPlatformSelect.hasMatch(line)) {\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" final String platformSelect = _lldbPlatformSelect.stringMatch(line) ?? '';\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" if (platformSelect.isEmpty) {\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" return;\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" }\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" final int promptEndIndex = line.indexOf(platformSelect);\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" if (promptEndIndex == -1) {\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" return;\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" }\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" final String prompt = line.substring(0, promptEndIndex);\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" lldbRun = RegExp(RegExp.escape(prompt) + r'\\s*run');\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" _logger.printTrace(line);\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" return;\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" }\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v("\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // (lldb) run\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // success\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // 2020-09-15 13:42:25.185474-0700 Runner[477:181141] flutter: The Dart VM service is listening on http://127.0.0.1:57782/\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" if (_lldbRun.hasMatch(line)) {\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" if (lldbRun.hasMatch(line)) {\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" ...\n")])])])])]),s("p",[t._v("修改完进行测试,确认没有问题后向 "),s("code",[t._v("Flutter")]),t._v(" 官方提交了 "),s("a",{attrs:{href:"https://github.com/flutter/flutter/pull/119443",target:"_blank",rel:"noopener noreferrer"}},[t._v("PR"),s("OutboundLink")],1)]),t._v(" "),s("h2",{attrs:{id:"五、调试"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#五、调试"}},[t._v("#")]),t._v(" 五、调试")]),t._v(" "),s("blockquote",[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("使用 "),s("code",[t._v("VSCode")]),t._v(" 打开 "),s("code",[t._v("flutter_tools")]),t._v(" 项目,调试前需要做的就两个操作:")]),t._v(" "),s("p",[t._v("1、调整 "),s("code",[t._v(".vscode/launch.json")])]),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('"name"')]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"flutter_tools"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token property"}},[t._v('"request"')]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"launch"')]),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('"dart"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token property"}},[t._v('"args"')]),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 string"}},[t._v('"run"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"-v"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"-d"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"设备id"')]),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('"program"')]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"bin/flutter_tools.dart"')]),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("2、指定运行的 "),s("code",[t._v("flutter")]),t._v(" 项目")]),t._v(" "),s("p",[t._v("上面只是设置了打印日志和指定运行的设备id,还差指定运行的 "),s("code",[t._v("flutter")]),t._v(" 项目。")]),t._v(" "),s("p",[t._v("由于 "),s("code",[t._v("flutter")]),t._v(" 命令里没有提供相关参数,所以我在 "),s("code",[t._v("lib/executable.dart")]),t._v(" 文件中 "),s("code",[t._v("Cache.flutterRoot")]),t._v(" 的上一行添加如下代码,强行修改当前执行 "),s("code",[t._v("flutter")]),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(".")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 设置当前执行flutter命令的目录路径")]),t._v("\nglobals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("fs"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("currentDirectory "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'/Users/lxf/Desktop/LXF/github/flutter_scrollview_observer/example'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Cache")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("flutterRoot "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Cache")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("defaultFlutterRoot")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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(".vscode/launch.json")]),t._v(" 配置 "),s("code",[t._v("cwd")]),t._v(" 即可达到效果,完整配置如下:")]),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('"name"')]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"flutter_tools"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token property"}},[t._v('"request"')]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"launch"')]),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('"dart"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token property"}},[t._v('"args"')]),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 string"}},[t._v('"run"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"-v"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"-d"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"设备id"')]),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('"program"')]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"${workspaceFolder}/bin/flutter_tools.dart"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token property"}},[t._v('"env"')]),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('"FLUTTER_ROOT"')]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"${workspaceFolder}/../../"')]),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('"cwd"')]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/gitHub/flutter_scrollview_observer/example"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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:"注意项"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#注意项"}},[t._v("#")]),t._v(" 注意项")]),t._v(" "),s("p",[t._v("当你调试完 "),s("code",[t._v("flutter_tools")]),t._v(" 的源码后,高高兴兴去到自己的 "),s("code",[t._v("flutter")]),t._v(" 工程目录下执行 "),s("code",[t._v("flutter run")]),t._v(" 时,你会发现改动不生效,这是因为存在如下缓存文件")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("flutter/bin/cache/flutter_tools.snapshot\n")])])]),s("p",[t._v("解决方式:将该文件删除即可,当执行任意的 "),s("code",[t._v("flutter")]),t._v(" 命令时会自动再次生成。")]),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("PR")]),t._v(" 的过程也学到了很多,过程中比较令人记忆深刻的是以下几点:")]),t._v(" "),s("ol",[s("li",[t._v("每一行的内容不可以以空格结尾,否则 "),s("code",[t._v("Linux analyze")]),t._v(" 这个 "),s("code",[t._v("Check")]),t._v(" 会失败。")]),t._v(" "),s("li",[s("code",[t._v("100+")]),t._v(" 条 "),s("code",[t._v("Check")]),t._v(" 执行时间是真的久,好像是 "),s("code",[t._v("40+分钟")]),t._v(",每次提交都会重新执行。")]),t._v(" "),s("li",[t._v("有的 "),s("code",[t._v("Check")]),t._v(" 时不时会抽风,像 "),s("code",[t._v("Google testing")]),t._v(" 有时会一直卡住,"),s("code",[t._v("luci-flutter")]),t._v(" 会经常失败,这些可以不用理它,因为其它人也会,当官方调整完会自动重新执行。")]),t._v(" "),s("li",[t._v("提交 "),s("code",[t._v("PR")]),t._v(" 后到开始审核的时间好久,"),s("code",[t._v("2023.1.29")]),t._v(" 提交,"),s("code",[t._v("2023.2.3")]),t._v(" 审核,5天,你知道我这5天是怎么过来的吗? 😂")])]),t._v(" "),s("p",[t._v("但不管怎么说,功夫不负有心人,最终于 "),s("code",[t._v("2023.2.7")]),t._v(" 成功 "),s("code",[t._v("Merge")]),t._v(",这是一次不错的开始 😃")])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/127.a6aa9d8a.js b/assets/js/127.a6aa9d8a.js new file mode 100644 index 000000000..68236285c --- /dev/null +++ b/assets/js/127.a6aa9d8a.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[127],{439:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h2",{attrs:{id:"一、背景"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#一、背景"}},[t._v("#")]),t._v(" 一、背景")]),t._v(" "),s("p",[t._v("上一篇 "),s("RouterLink",{attrs:{to:"/pages/5c4b50/"}},[t._v("Flutter - 我给官方提PR,解决run命令卡住问题 😃")]),t._v(" 中,虽然我提的 "),s("code",[t._v("pr")]),t._v(" 最终成功 "),s("code",[t._v("merge")]),t._v(" 了,但是我心中还存在着几点疑问,如:")],1),t._v(" "),s("ol",[s("li",[t._v("只能删除 "),s("code",[t._v("snapshot")]),t._v(" 文件才能使修改的 "),s("code",[t._v("flutter_tools")]),t._v(" 代码生效吗?")]),t._v(" "),s("li",[t._v("终端内执行 "),s("code",[t._v("flutter")]),t._v(" 命令后的流程是怎样的?")])]),t._v(" "),s("p",[t._v("现在就让我们带着疑问一起来了解一下从 "),s("code",[t._v("flutter run")]),t._v(" 到 "),s("code",[t._v("flutter_tools")]),t._v(" 调用 "),s("code",[t._v("launchAndAttach")]),t._v(" 的过程吧。")]),t._v(" "),s("h2",{attrs:{id:"二、终端"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#二、终端"}},[t._v("#")]),t._v(" 二、终端")]),t._v(" "),s("p",[t._v("流程图如下:")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202302192008218.png",alt:""}})]),t._v(" "),s("h3",{attrs:{id:"flutter-文件"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#flutter-文件"}},[t._v("#")]),t._v(" "),s("code",[t._v("flutter")]),t._v(" 文件")]),t._v(" "),s("p",[t._v("在终端内执行 "),s("code",[t._v("flutter")]),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 comment"}},[t._v("# export PATH=~/developer/flutter/bin:$PATH")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("export")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s("span",{pre:!0,attrs:{class:"token environment constant"}},[t._v("PATH")])]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=~")]),t._v("/fvm/default/bin:"),s("span",{pre:!0,attrs:{class:"token environment constant"}},[t._v("$PATH")]),t._v("\n")])])]),s("p",[t._v("在上述路径下找到名为 "),s("code",[t._v("flutter")]),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 shebang important"}},[t._v("#!/usr/bin/env bash")]),t._v("\n\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("# /Users/lxf/fvm/versions/3.1.0/bin/flutter")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("PROG_NAME")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$(")]),t._v("follow_links "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("${"),s("span",{pre:!0,attrs:{class:"token environment constant"}},[t._v("BASH_SOURCE")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("0"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("}")]),t._v('"')]),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v(")")])]),t._v('"')]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# /Users/lxf/fvm/versions/3.1.0/bin")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("BIN_DIR")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$(")]),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("cd")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("${PROG_NAME"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("*}")]),t._v('"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("pwd")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-P")]),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v(")")])]),t._v('"')]),t._v("\n\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# To define `shared::execute()` function")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("source")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$BIN_DIR")]),t._v('/internal/shared.sh"')]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# $@: 代表输入的所有参数,但是每个区分对待")]),t._v("\nshared::execute "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$@")]),t._v('"')]),t._v("\n")])])]),s("p",[t._v("将 "),s("code",[t._v("internal/shared.sh")]),t._v(" 脚本的内容引入,然后执行其 "),s("code",[t._v("shared::execute")]),t._v(" 方法,并通过 "),s("code",[t._v('"$@"')]),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 comment"}},[t._v("# 在终端上输入的内容")]),t._v("\nflutter run "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-v")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-d")]),t._v(" 设备id\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 实际上命令后续的参数是传递给了 shared::execute")]),t._v("\nshared::execute run "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-v")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-d")]),t._v(" 设备id\n")])])]),s("h3",{attrs:{id:"shared-sh-文件"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#shared-sh-文件"}},[t._v("#")]),t._v(" "),s("code",[t._v("shared.sh")]),t._v(" 文件")]),t._v(" "),s("p",[t._v("打开 "),s("code",[t._v("internal/shared.sh")]),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 shebang important"}},[t._v("#!/usr/bin/env bash")]),t._v("\n\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("function")]),t._v(" upgrade_flutter "),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 // 创建cache目录\n "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("mkdir")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-p")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$FLUTTER_ROOT")]),t._v('/bin/cache"')]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 取出当前flutter源码HEAD提交的SHA1值")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("local")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("revision")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$(")]),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("cd")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$FLUTTER_ROOT")]),t._v('"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("git")]),t._v(" rev-parse HEAD"),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v(")")])]),t._v('"')]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 拼接,如 bcea432bce54a83306b3c00a7ad0ed98f777348d:")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("local")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("compilekey")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$revision")]),t._v(":"),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$FLUTTER_TOOL_ARGS")]),t._v('"')]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 判断缓存是否需要更新的几个步骤:")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# * 没有snapshot文件, 或者")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# * stamp文件不存在, 或者")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# * stamp文件内容为空, 或者")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# * stamp文件内的SHA1与当前HEAD的SHA1不同, 或者")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# * pubspec.yaml 的修改时间比 pubspec.lock 的新")]),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("[")]),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 parameter variable"}},[t._v("-f")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$SNAPSHOT_PATH")]),t._v('"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("||")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-s")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$STAMP_PATH")]),t._v('"')]),t._v(" "),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 variable"}},[s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$(")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("cat")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$STAMP_PATH")]),t._v('"')]),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v(")")])]),t._v('"')]),t._v(" "),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 variable"}},[t._v("$compilekey")]),t._v('"')]),t._v(" "),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 variable"}},[t._v("$FLUTTER_TOOLS_DIR")]),t._v('/pubspec.yaml"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-nt")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$FLUTTER_TOOLS_DIR")]),t._v('/pubspec.lock"')]),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 keyword"}},[t._v("then")]),t._v("\n _wait_for_lock\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# A different shell process might have updated the tool/SDK.")]),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("[")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$SNAPSHOT_PATH")]),t._v('"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-s")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$STAMP_PATH")]),t._v('"')]),t._v(" "),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 variable"}},[s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$(")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("cat")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$STAMP_PATH")]),t._v('"')]),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v(")")])]),t._v('"')]),t._v(" "),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 variable"}},[t._v("$compilekey")]),t._v('"')]),t._v(" "),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 variable"}},[t._v("$FLUTTER_TOOLS_DIR")]),t._v('/pubspec.yaml"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-ot")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$FLUTTER_TOOLS_DIR")]),t._v('/pubspec.lock"')]),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 keyword"}},[t._v("then")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("exit")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$?")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("fi")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 拉取dart_sdk")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("rm")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$FLUTTER_ROOT")]),t._v('/version"')]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("touch")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$FLUTTER_ROOT")]),t._v('/bin/cache/.dartignore"')]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$FLUTTER_ROOT")]),t._v('/bin/internal/update_dart_sdk.sh"')]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),s("span",{pre:!0,attrs:{class:"token file-descriptor important"}},[t._v("&2")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("echo")]),t._v(" Building flutter tool"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Prepare packages...")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("VERBOSITY")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"--verbosity=error"')]),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("[")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$CI")]),t._v('"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"true"')]),t._v(" "),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 variable"}},[t._v("$BOT")]),t._v('"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"true"')]),t._v(" "),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 variable"}},[t._v("$CONTINUOUS_INTEGRATION")]),t._v('"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"true"')]),t._v(" "),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 variable"}},[t._v("$CHROME_HEADLESS")]),t._v('"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"1"')]),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 keyword"}},[t._v("then")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("PUB_ENVIRONMENT")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$PUB_ENVIRONMENT")]),t._v(':flutter_bot"')]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("VERBOSITY")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"--verbosity=normal"')]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("fi")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("export")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("PUB_ENVIRONMENT")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$PUB_ENVIRONMENT")]),t._v(':flutter_install"')]),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("[")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-d")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$FLUTTER_ROOT")]),t._v('/.pub-cache"')]),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 keyword"}},[t._v("then")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("export")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("PUB_CACHE")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"${PUB_CACHE:-"')]),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$FLUTTER_ROOT")]),t._v("/.pub-cache"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"}"')]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("fi")]),t._v("\n pub_upgrade_with_retry\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 编译产出snapshot文件")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$DART")]),t._v('"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--verbosity")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("error --disable-dart-dev "),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$FLUTTER_TOOL_ARGS")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--snapshot")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$SNAPSHOT_PATH")]),t._v('"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--packages")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$FLUTTER_TOOLS_DIR")]),t._v('/.packages"')]),t._v(" --no-enable-mirrors "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$SCRIPT_PATH")]),t._v('"')]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 将 compilekey 写入到 stamp 文件")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("echo")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$compilekey")]),t._v('"')]),t._v(" "),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 variable"}},[t._v("$STAMP_PATH")]),t._v('"')]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("fi")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("exit")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token variable"}},[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("# This function is intended to be executed by entrypoints (e.g. `//bin/flutter`")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# and `//bin/dart`). PROG_NAME and BIN_DIR should already be set by those")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# entrypoints.")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),t._v(" shared::"),s("span",{pre:!0,attrs:{class:"token function-name function"}},[t._v("execute")]),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 builtin class-name"}},[t._v("export")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("FLUTTER_ROOT")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$(")]),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("cd")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("${BIN_DIR}")]),t._v('/.."')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("pwd")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-P")]),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v(")")])]),t._v('"')]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# If present, run the bootstrap script first")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("BOOTSTRAP_PATH")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$FLUTTER_ROOT")]),t._v('/bin/internal/bootstrap.sh"')]),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(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$BOOTSTRAP_PATH")]),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 keyword"}},[t._v("then")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("source")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$BOOTSTRAP_PATH")]),t._v('"')]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("fi")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("FLUTTER_TOOLS_DIR")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$FLUTTER_ROOT")]),t._v('/packages/flutter_tools"')]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("SNAPSHOT_PATH")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$FLUTTER_ROOT")]),t._v('/bin/cache/flutter_tools.snapshot"')]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("STAMP_PATH")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$FLUTTER_ROOT")]),t._v('/bin/cache/flutter_tools.stamp"')]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("SCRIPT_PATH")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$FLUTTER_TOOLS_DIR")]),t._v('/bin/flutter_tools.dart"')]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("DART_SDK_PATH")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$FLUTTER_ROOT")]),t._v('/bin/cache/dart-sdk"')]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("DART")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$DART_SDK_PATH")]),t._v('/bin/dart"')]),t._v("\n\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("# 更新依赖、pub-cache目录")]),t._v("\n upgrade_flutter "),s("span",{pre:!0,attrs:{class:"token operator"}},[s("span",{pre:!0,attrs:{class:"token file-descriptor important"}},[t._v("7")]),t._v("<")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$PROG_NAME")]),t._v('"')]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# BIN_NAME 的值为 flutter")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("BIN_NAME")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$(")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("basename")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$PROG_NAME")]),t._v('"')]),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v(")")])]),t._v('"')]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$BIN_NAME")]),t._v('"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v("\n flutter*"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 执行 flutter_tools.snapshot")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# exec")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# /Users/lxf/fvm/versions/3.1.0/bin/cache/dart-sdk/bin/dart")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# --disable-dart-dev")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v('# --packages="/Users/lxf/fvm/versions/3.1.0/packages/flutter_tools/.packages"')]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# /Users/lxf/fvm/versions/3.1.0/bin/cache/flutter_tools.snapshot ")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("exec")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$DART")]),t._v('"')]),t._v(" --disable-dart-dev "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--packages")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$FLUTTER_TOOLS_DIR")]),t._v('/.packages"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$FLUTTER_TOOL_ARGS")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$SNAPSHOT_PATH")]),t._v('"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$@")]),t._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 dart*"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("exec")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$DART")]),t._v('"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$@")]),t._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 operator"}},[t._v(">")]),s("span",{pre:!0,attrs:{class:"token file-descriptor important"}},[t._v("&2")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("echo")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Error! Executable name '),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$BIN_NAME")]),t._v(' not recognized!"')]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("exit")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),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("esac")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("ul",[s("li",[s("code",[t._v("basename")]),t._v(":去掉文件名前面的目录并打印出来")]),t._v(" "),s("li",[s("code",[t._v("DART")]),t._v(":"),s("code",[t._v("dart")]),t._v(" 可执行文件的文件路径")]),t._v(" "),s("li",[s("code",[t._v("FLUTTER_TOOLS_DIR")]),t._v(":"),s("code",[t._v("flutter_tools")]),t._v(" 目录路径")]),t._v(" "),s("li",[s("code",[t._v("FLUTTER_TOOL_ARGS")]),t._v(":单独的空格分隔")]),t._v(" "),s("li",[s("code",[t._v("SNAPSHOT_PATH")]),t._v(":"),s("code",[t._v("flutter_tools.snapshot")]),t._v(" 文件路径")]),t._v(" "),s("li",[s("code",[t._v("SCRIPT_PATH")]),t._v(":编译的入口文件")])]),t._v(" "),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 builtin class-name"}},[t._v("exec")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n/Users/lxf/fvm/versions/3.1.0/bin/cache/dart-sdk/bin/dart "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n--disable-dart-dev "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--packages")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/fvm/versions/3.1.0/packages/flutter_tools/.packages"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),t._v("\n/Users/lxf/fvm/versions/3.1.0/bin/cache/flutter_tools.snapshot "),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("flutter_tools.snapshot")])]),t._v(" "),s("p",[t._v("当然了,在到这里之前会执行 "),s("code",[t._v("upgrade_flutter")]),t._v(" 方法,其主要是以下几个动作:")]),t._v(" "),s("ol",[s("li",[t._v("创建 "),s("code",[t._v("bin/cache")]),t._v(" 目录")]),t._v(" "),s("li",[t._v("拉取 "),s("code",[t._v("dart-sdk")])]),t._v(" "),s("li",[t._v("创建或更新 "),s("code",[t._v("flutter_tools.snapshot")])])]),t._v(" "),s("p",[t._v("为了确保 "),s("code",[t._v("flutter_tools.snapshot")]),t._v(' 是存在的且为 "最新",其中判断是否需要对缓存进行更新有几个点:')]),t._v(" "),s("ul",[s("li",[t._v("没有 "),s("code",[t._v("flutter_tools.snapshot")]),t._v(" 文件, 或者")]),t._v(" "),s("li",[s("code",[t._v("flutter_tools.stamp")]),t._v(" 文件不存在, 或者")]),t._v(" "),s("li",[s("code",[t._v("flutter_tools.stamp")]),t._v(" 文件内容为空, 或者")]),t._v(" "),s("li",[s("code",[t._v("flutter_tools.stamp")]),t._v(" 文件内的 "),s("code",[t._v("SHA1")]),t._v("与当前 "),s("code",[t._v("HEAD")]),t._v(" 的 "),s("code",[t._v("SHA1")]),t._v(" 不同, 或者")]),t._v(" "),s("li",[s("code",[t._v("pubspec.yaml")]),t._v(" 的修改时间比 "),s("code",[t._v("pubspec.lock")]),t._v(" 的新")])]),t._v(" "),s("p",[t._v("终端执行 "),s("code",[t._v("flutter")]),t._v(" 命令的流程也就到这了,这部分让我们知道了 "),s("code",[t._v("flutter")]),t._v(" 命令实际上就是去执行 "),s("code",[t._v("flutter_tools.snapshot")]),t._v(" 文件,以及在 "),s("code",[t._v("upgrade_flutter")]),t._v(" 方法的最后是去编译产出该 "),s("code",[t._v("snapshot")]),t._v(" 文件,其编译的入口文件为 "),s("code",[t._v("flutter/packages/flutter_tools/bin/flutter_tools.dart")]),t._v("。")]),t._v(" "),s("p",[t._v("那接下来让我们一起去看看 "),s("code",[t._v("flutter_tools")]),t._v(" 的源码。")]),t._v(" "),s("h2",{attrs:{id:"三、flutter-tools-源码"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#三、flutter-tools-源码"}},[t._v("#")]),t._v(" 三、flutter_tools 源码")]),t._v(" "),s("p",[t._v("下面以命令 "),s("code",[t._v("flutter run -v -d 设备ID")]),t._v(" 为例")]),t._v(" "),s("p",[t._v("整体流程图如下:")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202302192008275.png",alt:""}})]),t._v(" "),s("p",[t._v("温馨提醒:请放大食用,最好可以边对照流程图边看下面的源码分析")]),t._v(" "),s("h3",{attrs:{id:"_1、准备阶段"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、准备阶段"}},[t._v("#")]),t._v(" 1、准备阶段")]),t._v(" "),s("p",[t._v("在真正执行 "),s("code",[t._v("flutter run")]),t._v(" 命令前会有一个准备阶段")]),t._v(" "),s("h4",{attrs:{id:"入口文件"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#入口文件"}},[t._v("#")]),t._v(" 入口文件")]),t._v(" "),s("p",[s("code",[t._v("flutter_tools.dart")]),t._v(" 为 "),s("code",[t._v("flutter_tools")]),t._v(" 的入口文件,代码如下:")]),t._v(" "),s("blockquote",[s("p",[t._v("文件路径:"),s("code",[t._v("bin/flutter_tools.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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:flutter_tools/executable.dart'")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("as")]),t._v(" executable"),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("main")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" 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("\n executable"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("args"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("executable.dart")]),t._v(" 文件里的 "),s("code",[t._v("main")]),t._v(" 方法")]),t._v(" "),s("h4",{attrs:{id:"main"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#main"}},[t._v("#")]),t._v(" main")]),t._v(" "),s("blockquote",[s("p",[t._v("文件路径:"),s("code",[t._v("lib/executable.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 class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),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 class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" args"),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 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("await")]),t._v(" runner"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("run")]),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("\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 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("generateCommands")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n verbose"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" verbose"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("}")]),t._v("\n")])])]),s("p",[s("code",[t._v("generateCommands")]),t._v(" 生成的是 "),s("code",[t._v("List")]),t._v(",对应 "),s("code",[t._v("lib/src/commands")]),t._v(" 目录下的所有命令,这里面包含了我们需要关注的 "),s("code",[t._v("RunCommand")]),t._v(",对应 "),s("code",[t._v("flutter run")])]),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 class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("generateCommands")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n required bool verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n required bool verbose"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("AnalyzeCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fileSystem"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("fs"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" platform"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("platform"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" processManager"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("processManager"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" logger"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("logger"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" terminal"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("terminal"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" artifacts"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("artifacts"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" allProjectValidators"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ProjectValidator")]),s("span",{pre:!0,attrs:{class:"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 class-name"}},[t._v("AssembleCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" buildSystem"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("buildSystem"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("AttachCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("BuildCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("ChannelCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("CleanCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("verbose"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" verbose"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("ConfigCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("CustomDevicesCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("customDevicesConfig"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("customDevicesConfig"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" operatingSystemUtils"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),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(" terminal"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("terminal"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" platform"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("platform"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" featureFlags"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" featureFlags"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" processManager"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("processManager"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fileSystem"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("fs"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" logger"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("logger"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("CreateCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("DaemonCommand")]),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 operator"}},[t._v("!")]),t._v("verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("DebugAdapterCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("DevicesCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("DoctorCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("verbose"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" verbose"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("DowngradeCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("DriveCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fileSystem"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("fs"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" logger"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("logger"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" platform"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("platform"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("EmulatorsCommand")]),s("span",{pre:!0,attrs:{class:"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 class-name"}},[t._v("FormatCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("GenerateCommand")]),s("span",{pre:!0,attrs:{class:"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 class-name"}},[t._v("GenerateLocalizationsCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("fileSystem"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("fs"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" logger"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("logger"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("InstallCommand")]),s("span",{pre:!0,attrs:{class:"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 class-name"}},[t._v("LogsCommand")]),s("span",{pre:!0,attrs:{class:"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 class-name"}},[t._v("MakeHostAppEditableCommand")]),s("span",{pre:!0,attrs:{class:"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 class-name"}},[t._v("PackagesCommand")]),s("span",{pre:!0,attrs:{class:"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 class-name"}},[t._v("PrecacheCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" cache"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("cache"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" logger"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("logger"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" platform"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("platform"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" featureFlags"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" featureFlags"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("RunCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("ScreenshotCommand")]),s("span",{pre:!0,attrs:{class:"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 class-name"}},[t._v("ShellCompletionCommand")]),s("span",{pre:!0,attrs:{class:"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 class-name"}},[t._v("TestCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("UpgradeCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("SymbolizeCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("stdio"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("stdio"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fileSystem"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("fs"),s("span",{pre:!0,attrs:{class:"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("// Development-only commands. These are always hidden,")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("IdeConfigCommand")]),s("span",{pre:!0,attrs:{class:"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 class-name"}},[t._v("UpdatePackagesCommand")]),s("span",{pre:!0,attrs:{class:"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",[s("code",[t._v("runner.run")]),t._v(" 对应的是 "),s("code",[t._v("runner.dart")]),t._v(" 文件里的 "),s("code",[t._v("run")]),t._v(" 方法")]),t._v(" "),s("h4",{attrs:{id:"run"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#run"}},[t._v("#")]),t._v(" run")]),t._v(" "),s("blockquote",[s("p",[t._v("文件路径:"),s("code",[t._v("lib/runner.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 comment"}},[t._v("/// Runs the Flutter tool with support for the specified list of [commands].")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("int"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("run")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" args"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Function")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" commands"),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 bool muteCommandLogging "),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 bool verbose "),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 bool verboseHelp "),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 bool reportCrashes"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" flutterVersion"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Map")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Type")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Generator")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" overrides"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 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 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("return")]),t._v(" runInContext"),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("int"),s("span",{pre:!0,attrs:{class:"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 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 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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterCommandRunner")]),t._v(" runner "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterCommandRunner")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" verboseHelp"),s("span",{pre:!0,attrs:{class:"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("commands")]),s("span",{pre:!0,attrs:{class:"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("forEach")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("runner"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("addCommand"),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\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("getVersion")]),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(" flutterVersion "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("flutterVersion"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("getVersionString")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("redactUnknownBranches"),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(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Object")]),t._v(" firstError"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("StackTrace")]),t._v(" firstStackTrace"),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(" runZoned"),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("int"),s("span",{pre:!0,attrs:{class:"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(" "),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 "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" runner"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("run")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("args"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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(" onError"),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 class-name"}},[t._v("Object")]),t._v(" error"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("StackTrace")]),t._v(" stackTrace"),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(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ignore: deprecated_member_use")]),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(" overrides"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" overrides"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("runInContext")]),t._v(" 的运行结果,传入的参数为回调函数,下面会执行")]),t._v(" "),s("h4",{attrs:{id:"runincontext"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#runincontext"}},[t._v("#")]),t._v(" runInContext")]),t._v(" "),s("p",[s("code",[t._v("runInContext")]),t._v(" 方法的代码如下:")]),t._v(" "),s("blockquote",[s("p",[t._v("文件路径:lib/src/context_runner.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 class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("T")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" runInContext"),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("T")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("FutureOr")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("T")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Function")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" runner"),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("Map")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Type")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Generator")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" overrides"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 keyword"}},[t._v("async")]),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("// Wrap runner with any asynchronous initialization that should run with the")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// overrides and callbacks.")]),t._v("\n bool runningOnBot"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FutureOr")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("T")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("runnerWrapper")]),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 runningOnBot "),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(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isRunningOnBot"),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("runner")]),s("span",{pre:!0,attrs:{class:"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("return")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("run"),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("T")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'global fallbacks'")])]),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(" runnerWrapper"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n overrides"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" overrides"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n fallbacks"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Type")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Generator")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("返回了 "),s("code",[t._v("context.run")]),t._v(",并且将 "),s("code",[t._v("runnerWrapper")]),t._v(" 传递给了 "),s("code",[t._v("body")]),t._v(" 参数,而 "),s("code",[t._v("runnerWrapper")]),t._v(" 最终调用 "),s("code",[t._v("runner")]),t._v(",下面是 "),s("code",[t._v("context.run")]),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 class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("V")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" run"),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("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 required "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FutureOr")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("V")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Function")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" body"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" name"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Map")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Type")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Generator")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" overrides"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Map")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Type")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Generator")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" fallbacks"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ZoneSpecification")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" zoneSpecification"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 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("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AppContext")]),t._v(" child "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AppContext")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[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("\n name"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Map")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Type")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Generator")]),s("span",{pre:!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("unmodifiable")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("overrides "),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("const")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Type")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Generator")]),s("span",{pre:!0,attrs:{class:"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 class-name"}},[t._v("Map")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Type")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Generator")]),s("span",{pre:!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("unmodifiable")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("fallbacks "),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("const")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Type")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Generator")]),s("span",{pre:!0,attrs:{class:"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 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(" runZoned"),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("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("(")]),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 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("await")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("body")]),s("span",{pre:!0,attrs:{class:"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 zoneValues"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token generics"}},[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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AppContext")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),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(" child"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n zoneSpecification"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" zoneSpecification"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("body")]),t._v(",实际上就是执行 "),s("code",[t._v("runnerWrapper")]),t._v(",最终执行传进来的 "),s("code",[t._v("runner")]),t._v(" 回调函数。")]),t._v(" "),s("p",[t._v("在调用 "),s("code",[t._v("runInContext")]),t._v(" 时就传进了一个回调函数,在这个函数内创建了 "),s("code",[t._v("FlutterCommandRunner")]),t._v(" 实例 "),s("code",[t._v("runner")]),t._v(",调用 "),s("code",[t._v("commands")]),t._v(" 拿到上面提到的 "),s("code",[t._v("generateCommands")]),t._v(" 方法返回的所有 "),s("code",[t._v("FlutterCommand")]),t._v(",并通过 "),s("code",[t._v("addCommand")]),t._v(" 方法逐一添加到 "),s("code",[t._v("runner")]),t._v(" 实例的 "),s("code",[t._v("_commands 中")]),t._v(",以及将相应的 "),s("code",[t._v("command")]),t._v(" 和参数解析添加进 "),s("code",[t._v("argParser")]),t._v(" 实例。\n最后调用 "),s("code",[t._v("FlutterCommandRunner")]),t._v(" 实例 "),s("code",[t._v("runner")]),t._v(" 的 "),s("code",[t._v("run")]),t._v(" 方法。")]),t._v(" "),s("p",[t._v("我们先来看一下 "),s("code",[t._v("addCommand")]),t._v(" 方法")]),t._v(" "),s("h4",{attrs:{id:"commandrunner-addcommand"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#commandrunner-addcommand"}},[t._v("#")]),t._v(" CommandRunner.addCommand")]),t._v(" "),s("blockquote",[s("p",[t._v("文件路径:args/lib/command_runner.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 class-name"}},[t._v("Map")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Command")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("T")]),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("get")]),t._v(" commands "),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 class-name"}},[t._v("UnmodifiableMapView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("_commands"),s("span",{pre:!0,attrs:{class:"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(" _commands "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Command")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("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("{")]),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 class-name"}},[t._v("ArgParser")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("get")]),t._v(" argParser "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" _argParser"),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ArgParser")]),t._v(" _argParser"),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\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// Adds [Command] as a top-level command to this runner.")]),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("addCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Command")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("T")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" command"),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(" names "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("command"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name"),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("command"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("aliases"),s("span",{pre:!0,attrs:{class:"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("for")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" name "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" names"),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 _commands"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("name"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" command"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n argParser"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("addCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("name"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" command"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("argParser"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 command"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("_runner "),s("span",{pre:!0,attrs:{class:"token operator"}},[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("}")]),t._v("\n")])])]),s("p",[t._v("以命令的名字和别名分别做为 "),s("code",[t._v("key")]),t._v(","),s("code",[t._v("command")]),t._v(" 做为 "),s("code",[t._v("value")]),t._v(",这就是为什么执行命令时不论使用的是命令的名字还是别名,执行的结果都是一样的,因为都指向同一个 "),s("code",[t._v("Command")]),t._v("。")]),t._v(" "),s("p",[t._v("如 "),s("code",[t._v("ConfigCommand")]),t._v(" 对应的 "),s("code",[t._v("flutter config --help")]),t._v(" 或是 "),s("code",[t._v("flutter configure --help")]),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("ConfigCommand")]),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("FlutterCommand")]),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("ConfigCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" bool verboseHelp "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),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 argParser"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("addFlag")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'analytics'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" help"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Enable or disable reporting anonymously tool usage statistics and crash reports.'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n argParser"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("addFlag")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'clear-ios-signing-cert'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" negatable"),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(",")]),t._v(" help"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Clear the saved development certificate choice used to sign apps for iOS device deployment.'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n argParser"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("addOption")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'android-sdk'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" help"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'The Android SDK directory.'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n argParser"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("addOption")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'android-studio-dir'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" help"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'The Android Studio install directory.'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n argParser"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("addOption")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'build-dir'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" help"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'The relative path to override a projects build directory.'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" valueHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'out/'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n argParser"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("addFlag")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'machine'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" negatable"),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(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("hide")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" help"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Print config values as json.'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\t\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 punctuation"}},[t._v("}")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@override")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" name "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'config'")])]),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\n "),s("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@override")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" aliases "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'configure'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("FlutterCommandRunner")]),t._v(" 实例 "),s("code",[t._v("runner")]),t._v(" 的 "),s("code",[t._v("run")]),t._v(" 方法")]),t._v(" "),s("h4",{attrs:{id:"fluttercommandrunner-run"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#fluttercommandrunner-run"}},[t._v("#")]),t._v(" FlutterCommandRunner.run")]),t._v(" "),s("blockquote",[s("p",[t._v("文件路径:lib/src/runner/flutter_command_runner.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 metadata function"}},[t._v("@override")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("run")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Iterable")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" 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("\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("return")]),t._v(" "),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("run")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("args"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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",[s("code",[t._v("FlutterCommandRunner")]),t._v(" 的 "),s("code",[t._v("run")]),t._v(" 方法里主要还是调用了"),s("code",[t._v("super.run")]),t._v(","),s("code",[t._v("super.run")]),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 class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("T")]),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 function"}},[t._v("run")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Iterable")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" args"),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("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("sync")]),s("span",{pre:!0,attrs:{class:"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("runCommand")]),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("args"),s("span",{pre:!0,attrs:{class:"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\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ArgResults")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("parse")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Iterable")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" 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("\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("return")]),t._v(" argParser"),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("args"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ArgParserException")]),t._v(" "),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("error"),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("error"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("commands"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isEmpty"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("usageException")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("error"),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\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" command "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" commands"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("error"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("commands"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("first"),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 punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" commandName "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" error"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("commands"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("skip")]),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(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n command "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" command"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("subcommands"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("commandName"),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 punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n command"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("usageException")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("error"),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("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("其先帮我们调用 "),s("code",[t._v("argParser.parse")]),t._v(" 解析 "),s("code",[t._v("args")]),t._v(",再将解析后的数据传递给 "),s("code",[t._v("runCommand")]),t._v(" 方法")]),t._v(" "),s("h4",{attrs:{id:"fluttercommandrunner-runcommand"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#fluttercommandrunner-runcommand"}},[t._v("#")]),t._v(" FlutterCommandRunner.runCommand")]),t._v(" "),s("p",[s("code",[t._v("runCommand")]),t._v(" 方法")]),t._v(" "),s("blockquote",[s("p",[t._v("文件路径:lib/src/runner/flutter_command_runner.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 metadata function"}},[t._v("@override")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("runCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ArgResults")]),t._v(" topLevelResults"),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 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("await")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("run"),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n overrides"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" contextOverrides"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("map"),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Type")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Generator")]),s("span",{pre:!0,attrs:{class:"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 class-name"}},[t._v("Type")]),t._v(" type"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Object")]),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(" "),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 class-name"}},[t._v("MapEntry")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Type")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Generator")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),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 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(" value"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 body"),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 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 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("await")]),t._v(" "),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("runCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("topLevelResults"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("注意:这里的 "),s("code",[t._v("runCommand")]),t._v(" 是 "),s("code",[t._v("FlutterCommandRunner")]),t._v(" 内重写了父类的 "),s("code",[t._v("runCommand")]),t._v(",最后在 "),s("code",[t._v("body")]),t._v(" 内调用了父类("),s("code",[t._v("CommandRunner")]),t._v(")的 "),s("code",[t._v("runCommand")]),t._v(" 方法。")]),t._v(" "),s("h4",{attrs:{id:"commandrunner-runcommand"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#commandrunner-runcommand"}},[t._v("#")]),t._v(" CommandRunner.runCommand")]),t._v(" "),s("blockquote",[s("p",[t._v("文件路径:args/lib/command_runner.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 class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("T")]),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 function"}},[t._v("runCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ArgResults")]),t._v(" topLevelResults"),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("var")]),t._v(" argResults "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" topLevelResults"),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(" commands "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" _commands"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Command")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" command"),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(" commandString "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" executableName"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("while")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("commands"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isNotEmpty"),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 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("// 取出匹配的command")]),t._v("\n argResults "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" argResults"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("command"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n command "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" commands"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("argResults"),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 operator"}},[t._v("!")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n command"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("_globalResults "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" topLevelResults"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n command"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("_argResults "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" argResults"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 取出子命令数组赋值给commands")]),t._v("\n commands "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" command"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("_subcommands "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("as")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Map")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Command")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("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 commandString "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("' ")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("${")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("argResults"),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 string"}},[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("argResults"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("options"),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("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'help'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" argResults"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'help'")])]),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 command"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("printUsage")]),s("span",{pre:!0,attrs:{class:"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 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("\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\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),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(" command"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("run")]),s("span",{pre:!0,attrs:{class:"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("as")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("T")]),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("}")]),t._v("\n")])])]),s("p",[t._v("匹配出 "),s("code",[t._v("run")]),t._v(" 命令对应 "),s("code",[t._v("RunCommand")]),t._v(" 实例的 "),s("code",[t._v("command")]),t._v(",并执行其 "),s("code",[t._v("run")]),t._v(" 方法,即最后一行的 "),s("code",[t._v("command.run()")]),t._v("。")]),t._v(" "),s("p",[t._v("注:"),s("code",[t._v("RunCommand")]),t._v(" 继承自 "),s("code",[t._v("FlutterCommand")]),t._v(",且没有重写 "),s("code",[t._v("run")]),t._v(" 方法,所以这里的 "),s("code",[t._v("command.run()")]),t._v(" 调用的就是 "),s("code",[t._v("FlutterCommand")]),t._v(" 的 "),s("code",[t._v("run")]),t._v(" 方法。")]),t._v(" "),s("h3",{attrs:{id:"_2、命令执行"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、命令执行"}},[t._v("#")]),t._v(" 2、命令执行")]),t._v(" "),s("h4",{attrs:{id:"fluttercommand-run"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#fluttercommand-run"}},[t._v("#")]),t._v(" FlutterCommand.run")]),t._v(" "),s("blockquote",[s("p",[t._v("文件路径:lib/src/runner/flutter_command.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 metadata function"}},[t._v("@override")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("run")]),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 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("return")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("run"),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'command'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n overrides"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Type")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Generator")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterCommand")]),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 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("this")]),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 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 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("try")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n commandResult "),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("verifyThenRunCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("commandPath"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("finally")]),t._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("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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",[s("code",[t._v("verifyThenRunCommand")]),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 function"}},[t._v("@mustCallSuper")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterCommandResult")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("verifyThenRunCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" commandPath"),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 globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("preRunValidator"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("validate")]),s("span",{pre:!0,attrs:{class:"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("// 更新cache下的artifacts")]),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("shouldUpdateCache"),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("// First always update universal artifacts, as some of these (e.g.")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ios-deploy on macOS) are required to determine `requiredArtifacts`.")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" bool offline"),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("argParser"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("options"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("containsKey")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'offline'")])]),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 offline "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("boolArg")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'offline'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 offline "),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("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("cache"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("updateAll")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DevelopmentArtifact")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DevelopmentArtifact")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("universal"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" offline"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" offline"),s("span",{pre:!0,attrs:{class:"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(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("cache"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("updateAll")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" requiredArtifacts"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" offline"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" offline"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("cache"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("releaseLock")]),s("span",{pre:!0,attrs:{class:"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("// 查找 pubspec.yaml 并更正项目路径")]),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("validateCommand")]),s("span",{pre:!0,attrs:{class:"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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterProject")]),t._v(" project "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterProject")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("current")]),s("span",{pre:!0,attrs:{class:"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 project"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("checkForDeprecation")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("deprecationBehavior"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" deprecationBehavior"),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("shouldRunPub"),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Environment")]),t._v(" environment "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Environment")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n artifacts"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("artifacts"),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 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("await")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("generateLocalizationsSyntheticPackage")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n environment"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" environment"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n buildSystem"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("buildSystem"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 对项目执行 pub get,下载依赖")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" pub"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("get")]),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PubContext")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("getVerifyContext")]),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 generateSyntheticPackage"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" project"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("manifest"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("generateSyntheticPackage"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n checkUpToDate"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" cachePubGet"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("await")]),t._v(" project"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("regeneratePlatformSpecificTooling")]),s("span",{pre:!0,attrs:{class:"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("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("reportNullSafety"),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("await")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_sendNullSafetyAnalyticsEvents")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("project"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 function"}},[t._v("setupApplicationPackages")]),s("span",{pre:!0,attrs:{class:"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("commandPath "),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 "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Usage")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("command")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("commandPath"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" parameters"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CustomDimensions")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n commandHasTerminal"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("stdio"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("hasTerminal"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("merge")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" usageValues"),s("span",{pre:!0,attrs:{class:"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 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("runCommand")]),s("span",{pre:!0,attrs:{class:"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("这个方法主要做了以下几点:")]),t._v(" "),s("ol",[s("li",[t._v("更新 "),s("code",[t._v("flutter/bin/cache")]),t._v(" 下的 "),s("code",[t._v("artifacts")])]),t._v(" "),s("li",[t._v("校验当前目录下是否有 "),s("code",[t._v("pubspec.yaml")]),t._v(" 文件,没有就往父路径里找,并修正项目路径")]),t._v(" "),s("li",[t._v("执行 "),s("code",[t._v("pub get")]),t._v(" 下载项目依赖")]),t._v(" "),s("li",[s("code",[t._v("runCommand()")]),t._v(" 执行的是 "),s("code",[t._v("RunCommand")]),t._v(" 下的 "),s("code",[t._v("runCommand")])])]),t._v(" "),s("p",[t._v("注:从这个地方开始才真正去执行 "),s("code",[t._v("run")]),t._v(" 命令!")]),t._v(" "),s("h4",{attrs:{id:"runcommand-runcommand"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#runcommand-runcommand"}},[t._v("#")]),t._v(" RunCommand.runCommand")]),t._v(" "),s("blockquote",[s("p",[t._v("文件路径:lib/src/commands/run.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 metadata function"}},[t._v("@override")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterCommandResult")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("runCommand")]),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("// Enable hot mode by default if `--no-hot` was not passed and we are in")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// debug mode.")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildInfo")]),t._v(" buildInfo "),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("getBuildInfo")]),s("span",{pre:!0,attrs:{class:"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("final")]),t._v(" bool hotMode "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("shouldUseHotMode")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("buildInfo"),s("span",{pre:!0,attrs:{class:"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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" applicationBinaryPath "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("stringArg")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterOptions")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("kUseApplicationBinary"),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\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 根据 hotMode 来创建 HotRunner 或 ColdRunner")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ResidentRunner")]),t._v(" runner "),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("createRunner")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n applicationBinaryPath"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" applicationBinaryPath"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n flutterDevices"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" flutterDevices"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n flutterProject"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" flutterProject"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n hotMode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" hotMode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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\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("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(" runner"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("run")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n appStartedCompleter"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" appStartedTimeRecorder"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n enableDevTools"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" stayResident "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("boolArg")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("kEnableDevTools"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n route"),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("\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(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("on")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("RPCError")]),t._v(" "),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("error"),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 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 keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterCommandResult")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ExitStatus")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("success"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n timingLabelParts"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"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("hotMode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'hot'")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'cold'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("getModeName")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("getBuildMode")]),s("span",{pre:!0,attrs:{class:"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(".")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 endTimeOverride"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" appStartedTime"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("hotMode")]),t._v(" 来创建 "),s("code",[t._v("HotRunner")]),t._v(" 或 "),s("code",[t._v("ColdRunner")]),t._v("(忽略 "),s("code",[t._v("web")]),t._v("~),然后执行 "),s("code",[t._v("HotRunner")]),t._v(" 的 "),s("code",[t._v("run")]),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 function"}},[t._v("@visibleForTesting")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ResidentRunner")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("createRunner")]),s("span",{pre:!0,attrs:{class:"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 function"}},[t._v("@required")]),t._v(" bool hotMode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@required")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterDevice")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" flutterDevices"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@required")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" applicationBinaryPath"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@required")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterProject")]),t._v(" flutterProject"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 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("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("hotMode "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("webMode"),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 class-name"}},[t._v("HotRunner")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("}")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),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("webMode"),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(" webRunnerFactory"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("createWebRunner")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ColdRunner")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("}")]),t._v("\n")])])]),s("p",[t._v("那 "),s("code",[t._v("hotMode")]),t._v(" 是怎么来的呢?我们点进 "),s("code",[t._v("shouldUseHotMode")]),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 class-name"}},[t._v("ArgResults")]),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(" argResults "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" _argResults"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ArgResults")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" _argResults"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nbool "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("boolArgDeprecated")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" name"),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(" argResults"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("name"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("as")]),t._v(" bool"),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 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("\nbool "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("get")]),t._v(" traceStartup "),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("boolArgDeprecated")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'trace-startup'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\nbool "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("shouldUseHotMode")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildInfo")]),t._v(" buildInfo"),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(" bool hotArg "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("boolArgDeprecated")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'hot'")])]),s("span",{pre:!0,attrs:{class:"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(" bool shouldUseHotMode "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" hotArg "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("traceStartup"),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(" buildInfo"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isDebug "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" shouldUseHotMode"),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("hot")]),t._v(" 和 "),s("code",[t._v("trace-startup")]),t._v(" 是从 "),s("code",[t._v("_argResults")]),t._v(" 取值,而 "),s("code",[t._v("ArgResults")]),t._v(" 内部实现 "),s("code",[t._v("[]")]),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("ArgResults")]),t._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 keyword"}},[t._v("dynamic")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("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("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" name"),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("(")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("_parser"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("options"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("containsKey")]),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(" "),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 class-name"}},[t._v("ArgumentError")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Could not find an option named \"")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("$")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("name")])]),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 "),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(" _parser"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("options"),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 operator"}},[t._v("!")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("valueOrDefault")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("_parsed"),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(")")]),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 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("hot")]),t._v(" 和 "),s("code",[t._v("trace-startup")]),t._v(" 是从 "),s("code",[t._v("options")]),t._v(" 取值")]),t._v(" "),s("p",[t._v("而 "),s("code",[t._v("RunCommand")]),t._v(" 在被实例化时就把 "),s("code",[t._v("hot")]),t._v(" 和 "),s("code",[t._v("trace-startup")]),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(" bool kHotReloadDefault "),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\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("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("RunCommandBase")]),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("FlutterCommand")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DeviceBasedDevelopmentArtifacts")]),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("RunCommandBase")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" required bool verboseHelp "),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 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 argParser\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("addFlag")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'trace-startup'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n negatable"),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(",")]),t._v("\n help"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Trace application startup, then exit, saving the trace to a file. '")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'By default, this will be saved in the \"build\" directory. If the '")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'FLUTTER_TEST_OUTPUTS_DIR environment variable is set, the file '")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'will be written there instead.'")])]),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 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(".")]),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("class")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("RunCommand")]),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("RunCommandBase")]),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("RunCommand")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" bool verboseHelp "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),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(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("super")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("verboseHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" verboseHelp"),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 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 function"}},[t._v("addFlag")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'hot'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n defaultsTo"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" kHotReloadDefault"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n help"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Run with support for hot reloading. Only available for debug mode. Not available with \"--trace-startup\".'")])]),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 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 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("addFlag")]),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("addFlag")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" name"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" abbr"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" help"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n bool"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" defaultsTo "),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 bool negatable "),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("void")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Function")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("bool"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" callback"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n bool "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("hide")]),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 class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" aliases "),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 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 punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_addOption")]),s("span",{pre:!0,attrs:{class:"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(")")]),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 punctuation"}},[t._v(".")]),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("_addOption")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" name"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" abbr"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" help"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" valueHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Iterable")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" allowed"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Map")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" allowedHelp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n defaultsTo"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Function")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" callback"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("OptionType")]),t._v(" type"),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("bool negatable "),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 bool"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" splitCommas"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n bool mandatory "),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 bool "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("hide")]),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 class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" aliases "),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 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 punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" allNames "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("name"),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("aliases"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" option "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("newOption")]),s("span",{pre:!0,attrs:{class:"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(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n _options"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("name"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" option"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n _optionsAndSeparators"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("option"),s("span",{pre:!0,attrs:{class:"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("for")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" alias "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" aliases"),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 _aliases"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("alias"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" name"),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("addFlag")]),t._v(" 方法其实是在往 "),s("code",[t._v("_options")]),t._v(" 里添加 "),s("code",[t._v("option")]),t._v(",而 "),s("code",[t._v("RunCommand")]),t._v(" 在初始化的时候就已经做了这个操作。")]),t._v(" "),s("p",[s("code",[t._v("options")]),t._v(" 和 "),s("code",[t._v("_options")]),t._v(" 两者的关系:"),s("code",[t._v("options")]),t._v(" 是 "),s("code",[t._v("_options")]),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("ArgParser")]),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Map")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Option")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" _options"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// The options that have been defined for this parser.")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Map")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Option")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" options"),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("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ArgParser")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Map")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Option")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" options"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Map")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ArgParser")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" commands"),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("_aliases"),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("bool allowTrailingOptions "),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(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("this")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("usageLineLength"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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(" _options "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" options"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n options "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UnmodifiableMapView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("options"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("options")]),t._v(" 取值其实就是对 "),s("code",[t._v("_options")]),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("// Enable hot mode by default if `--no-hot` was not passed and we are in")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// debug mode.")]),t._v("\n")])])]),s("p",[s("code",[t._v("hot")]),t._v(" 选项默认为 "),s("code",[t._v("true")]),t._v(",如果你想将选项 "),s("code",[t._v("hot")]),t._v(" 置为 "),s("code",[t._v("false")]),t._v(",可以使用选项 "),s("code",[t._v("--no-hot")]),t._v(",这是官方的 "),s("a",{attrs:{href:"https://pub.dev/packages/args",target:"_blank",rel:"noopener noreferrer"}},[t._v("args"),s("OutboundLink")],1),t._v(" 库提供的语法")]),t._v(" "),s("p",[t._v("再来看一下 "),s("code",[t._v("buildInfo.isDebug")])]),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("BuildInfo")]),t._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 bool "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("get")]),t._v(" isDebug "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" mode "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildMode")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("debug"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("hotMode")]),t._v(" 的值由 "),s("code",[t._v("hot")]),t._v(" 选项、"),s("code",[t._v("trace-startup")]),t._v(" 选项和当前的编译模式共同决定。")]),t._v(" "),s("h4",{attrs:{id:"hotrunner-run"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#hotrunner-run"}},[t._v("#")]),t._v(" HotRunner.run")]),t._v(" "),s("blockquote",[s("p",[t._v("文件路径:lib/src/run_hot.dart")])]),t._v(" "),s("p",[s("code",[t._v("HotRunner")]),t._v(" 的 "),s("code",[t._v("run")]),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 function"}},[t._v("@override")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("int"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("run")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("Completer")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DebugConnectionInfo")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" connectionInfoCompleter"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Completer")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" appStartedCompleter"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n bool enableDevTools "),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 class-name"}},[t._v("String")]),t._v(" route"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 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 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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("bool"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" startupTasks "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("bool"),s("span",{pre:!0,attrs:{class:"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("for")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterDevice")]),t._v(" device "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" flutterDevices"),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("await")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("runSourceGenerators")]),s("span",{pre:!0,attrs:{class:"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("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("device"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("generator "),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 "),s("span",{pre:!0,attrs:{class:"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 startupTasks"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),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 comment"}},[t._v("// outputPath: 编译产物路径,如:xx/xx/app.dill")]),t._v("\n device"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("generator"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("recompile")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n mainFile"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("uri"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Uri")]),s("span",{pre:!0,attrs:{class:"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 suppressErrors"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" applicationBinary "),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 checkDartPluginRegistry"),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("\n outputPath"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" dillOutputPath "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("getDefaultApplicationKernelPath")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n trackWidgetCreation"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" debuggingOptions"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("buildInfo"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("trackWidgetCreation"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 packageConfig"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" debuggingOptions"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("buildInfo"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("packageConfig"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n projectRootPath"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterProject")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("current")]),s("span",{pre:!0,attrs:{class:"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("directory"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("absolute"),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 fs"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" globals"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("fs"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("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 class-name"}},[t._v("CompilerOutput")]),t._v(" output"),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 compileTimer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("stop")]),s("span",{pre:!0,attrs:{class:"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 totalCompileTime "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+=")]),t._v(" compileTimer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("elapsed"),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(" output"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("errorCount "),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 "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Stopwatch")]),t._v(" launchAppTimer "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Stopwatch")]),s("span",{pre:!0,attrs:{class:"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("start")]),s("span",{pre:!0,attrs:{class:"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 startupTasks"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("device"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("runHot")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n hotRunner"),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 route"),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("\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("then")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("int 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 totalLaunchAppTime "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+=")]),t._v(" launchAppTimer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("elapsed"),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 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 "),s("span",{pre:!0,attrs:{class:"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\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 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("attach")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n connectionInfoCompleter"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" connectionInfoCompleter"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n appStartedCompleter"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" appStartedCompleter"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n enableDevTools"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" enableDevTools"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("一共执行了两个任务:")]),t._v(" "),s("ul",[s("li",[t._v("执行 "),s("code",[t._v("device.generator.recompile")]),t._v(",从项目中的 "),s("code",[t._v("main.dart")]),t._v(" 开始进行编译,产出 "),s("code",[t._v("app.dill")]),t._v(" 文件")]),t._v(" "),s("li",[t._v("执行 "),s("code",[t._v("device.runHot")]),t._v(",编译运行 "),s("code",[t._v("App")])])]),t._v(" "),s("h4",{attrs:{id:"flutterdevice-runhot"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#flutterdevice-runhot"}},[t._v("#")]),t._v(" FlutterDevice.runHot")]),t._v(" "),s("blockquote",[s("p",[t._v("文件路径:lib/src/resident_runner.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 class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("int"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("runHot")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("HotRunner")]),t._v(" hotRunner"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" route"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 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 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("// Start the application.")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LaunchResult")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" futureResult "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" device"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("startApp")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n package"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n mainPath"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" hotRunner"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("mainPath"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n debuggingOptions"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" hotRunner"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("debuggingOptions"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n platformArgs"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" platformArgs"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n route"),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("\n prebuiltApplication"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" prebuiltMode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n ipv6"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" hotRunner"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ipv6"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n userIdentifier"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" userIdentifier"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 \n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LaunchResult")]),t._v(" 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(" futureResult"),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("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("p",[t._v("这里的 "),s("code",[t._v("device.runHot")]),t._v(" 以 "),s("code",[t._v("iOS")]),t._v(" 的为例,所以此时 "),s("code",[t._v("device")]),t._v(" 的类型为 "),s("code",[t._v("IOSDevice")]),t._v(",找到该类的 "),s("code",[t._v("startApp")]),t._v(" 方法")]),t._v(" "),s("blockquote",[s("p",[t._v("文件路径:lib/src/ios/devices.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 metadata function"}},[t._v("@override")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LaunchResult")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("startApp")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("IOSApp")]),t._v(" package"),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("String")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" mainPath"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" route"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n required "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DebuggingOptions")]),t._v(" debuggingOptions"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Map")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Object")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" platformArgs "),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 generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Object")]),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("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n bool prebuiltApplication "),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 bool ipv6 "),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 class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" userIdentifier"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@visibleForTesting")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Duration")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" discoveryTimeout"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 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 class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" packageId"),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("(")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("prebuiltApplication"),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 _logger"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("printTrace")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Building ")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("${")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("package"),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 string"}},[t._v(" for ")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("$")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("id")])]),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\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Step 1: 编译xcode项目")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("XcodeBuildResult")]),t._v(" buildResult "),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("buildXcodeProject")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n app"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" package "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("as")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildableIOSApp")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n buildInfo"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" debuggingOptions"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("buildInfo"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n targetOverride"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" mainPath"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n activeArch"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" cpuArchitecture"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n deviceID"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" id"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 packageId "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" buildResult"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("xcodeBuildExecution"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("buildSettings"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'PRODUCT_BUNDLE_IDENTIFIER'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 packageId "),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 operator"}},[t._v("=")]),t._v(" package"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("id"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Step 2: 检查编译后的app文件是否存在")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Directory")]),t._v(" bundle "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" _fileSystem"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("directory")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("package"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("deviceBundlePath"),s("span",{pre:!0,attrs:{class:"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("(")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("bundle"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("existsSync")]),s("span",{pre:!0,attrs:{class:"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 _logger"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("printError")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Could not find the built application bundle at ")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("${")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("bundle"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("path")]),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 "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LaunchResult")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("failed")]),s("span",{pre:!0,attrs:{class:"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 comment"}},[t._v("// Step 3: 尝试给设备安装app")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" dartVmFlags "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("computeDartVmFlags")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("debuggingOptions"),s("span",{pre:!0,attrs:{class:"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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" launchArguments "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'--enable-dart-profiling'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Status")]),t._v(" installStatus "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" _logger"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("startProgress")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Installing and launching...'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("try")]),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("ProtocolDiscovery")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" observatoryDiscovery"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n int installationResult "),s("span",{pre:!0,attrs:{class:"token operator"}},[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 "),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("debuggingOptions"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("debuggingEnabled"),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 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("// 调试模式下开启observatory服务")]),t._v("\n observatoryDiscovery "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ProtocolDiscovery")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("observatory")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n deviceLogReader"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n portForwarder"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" portForwarder"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n hostPort"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" debuggingOptions"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("hostVmServicePort"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n devicePort"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" debuggingOptions"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("deviceVmServicePort"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n ipv6"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" ipv6"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n logger"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" _logger"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 comment"}},[t._v("// 通过ios-deploy安装App到设备(并附加)")]),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("iosDeployDebugger "),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 installationResult "),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(" _iosDeploy"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("launchApp")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n deviceId"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" id"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n bundlePath"),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("path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n appDeltaDirectory"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" package"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("appDeltaDirectory"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n launchArguments"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" launchArguments"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n interfaceType"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" interfaceType"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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(" "),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 installationResult "),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(" iosDeployDebugger"),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("launchAndAttach")]),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 number"}},[t._v("0")]),t._v(" "),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 "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LaunchResult")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("succeeded")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("observatoryUri"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" localUri"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ProcessException")]),t._v(" "),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 "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" iosDeployDebugger"),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("stopAndDumpBacktrace")]),s("span",{pre:!0,attrs:{class:"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 _logger"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("printError")]),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 keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LaunchResult")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("failed")]),s("span",{pre:!0,attrs:{class:"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("finally")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n installStatus"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("stop")]),s("span",{pre:!0,attrs:{class:"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("这部分的几个主要操作:")]),t._v(" "),s("ol",[s("li",[t._v("编译 "),s("code",[t._v("Xcode")]),t._v(" 项目")]),t._v(" "),s("li",[t._v("检查编译后的 "),s("code",[t._v("app")]),t._v(" 文件是否存在")]),t._v(" "),s("li",[t._v("调试模式下开启 "),s("code",[t._v("observatory")]),t._v(" 服务")]),t._v(" "),s("li",[t._v("通过 "),s("code",[t._v("ios-deploy")]),t._v(" 安装 "),s("code",[t._v("App")]),t._v(" 到设备(并附加)")])]),t._v(" "),s("p",[t._v("在第4点所对应的代码处,会调用 "),s("code",[t._v("launchAndAttach")]),t._v(" 方法,即上一篇中 "),s("code",[t._v("pr")]),t._v(" 主要修改的地方。")]),t._v(" "),s("p",[t._v("到此对 "),s("code",[t._v("flutter run")]),t._v(" 命令的探索就结束了,相信大家对开头提出的疑问已经有了答案,感谢你的耐心观看。")])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/128.5f11baed.js b/assets/js/128.5f11baed.js new file mode 100644 index 000000000..d9501c772 --- /dev/null +++ b/assets/js/128.5f11baed.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[128],{440:function(t,s,e){"use strict";e.r(s);var a=e(8),n=Object(a.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h2",{attrs:{id:"一、depot-tools"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#一、depot-tools"}},[t._v("#")]),t._v(" 一、depot_tools")]),t._v(" "),s("blockquote",[s("p",[s("a",{attrs:{href:"https://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools_tutorial.html#_setting_up",target:"_blank",rel:"noopener noreferrer"}},[t._v("depot_tools"),s("OutboundLink")],1),t._v(" 是调试 "),s("code",[t._v("Flutter")]),t._v(" 引擎的必备工具包,包含了 "),s("code",[t._v("gclient")]),t._v("、"),s("code",[t._v("gn")]),t._v(" 和 "),s("code",[t._v("ninja")]),t._v(" 等工具,这些在下面会用到!")])]),t._v(" "),s("p",[t._v("拉取 "),s("code",[t._v("depot_tools")]),t._v(" 到本地,我一般是放到 "),s("code",[t._v("~/developer/")]),t._v(" 下")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("$ "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("git")]),t._v(" clone https://chromium.googlesource.com/chromium/tools/depot_tools.git\n")])])]),s("p",[t._v("将 "),s("code",[t._v("depot_tools")]),t._v(" 的路径补充到 "),s("code",[t._v("PATH")])]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("$ "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("export")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[s("span",{pre:!0,attrs:{class:"token environment constant"}},[t._v("PATH")])]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("/path/to/depot_tools:"),s("span",{pre:!0,attrs:{class:"token environment constant"}},[t._v("$PATH")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 如: export PATH=~/developer/depot_tools:$PATH")]),t._v("\n")])])]),s("h2",{attrs:{id:"二、配置-gclient"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#二、配置-gclient"}},[t._v("#")]),t._v(" 二、配置.gclient")]),t._v(" "),s("p",[t._v("创建 "),s("code",[t._v("engine")]),t._v(" 目录,且终端进入到该目录,生成并配置 "),s("code",[t._v(".gclient")]),t._v(" 文件")]),t._v(" "),s("div",{staticClass:"language-json extra-class"},[s("pre",{pre:!0,attrs:{class:"language-json"}},[s("code",[t._v("➜ ~ cd /Users/lxf/Desktop/LXF/engine\n➜ engine touch .gclient\n➜ engine vim .gclient\n")])])]),s("p",[t._v("配置内容:")]),t._v(" "),s("div",{staticClass:"language-json extra-class"},[s("pre",{pre:!0,attrs:{class:"language-json"}},[s("code",[t._v("solutions = "),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 property"}},[t._v('"managed"')]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" False"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token property"}},[t._v('"name"')]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"src/flutter"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token property"}},[t._v('"url"')]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"git@github.com:flutter/engine.git@1837b5be5f0f1376a1ccf383950e83a80177fb4e"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token property"}},[t._v('"custom_deps"')]),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("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token property"}},[t._v('"deps_file"')]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"DEPS"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token property"}},[t._v('"safesync_url"')]),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 punctuation"}},[t._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("flutter")]),t._v(" 的,但如果你想后面提 "),s("code",[t._v("PR")]),t._v(",则需要使用自己 "),s("code",[t._v("fork")]),t._v(" 后的链接,如:"),s("code",[t._v("git@github.com:LinXunFeng/engine.git")])]),t._v(" "),s("p",[t._v("引擎 "),s("code",[t._v("git")]),t._v(" 地址后面携带的 "),s("code",[t._v("commit id")]),t._v(" 最好与当前 "),s("code",[t._v("Flutter")]),t._v(" 所使用的引擎版本保持一致,可以通过如下命令获取")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("flutter "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--version")]),t._v("\n")])])]),s("p",[t._v("输出:")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("Flutter "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("3.7")]),t._v(".7 • channel stable • \n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".\nEngine • revision 1837b5be5f\n"),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("flutter/bin/internal/engine.version\n\n# 1837b5be5f0f1376a1ccf383950e83a80177fb4e\n")])])]),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(".gclient")]),t._v(" 所在目录,即上面创建的 "),s("code",[t._v("engine")]),t._v(" 目录,执行如下命令")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("gclient "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("sync")]),t._v("\n")])])]),s("p",[t._v("注:"),s("code",[t._v("gclient")]),t._v(" 命令来自于 "),s("code",[t._v("depot_tools")])]),t._v(" "),s("p",[t._v("然后坐等完成即可,如果失败了就反复执行 "),s("code",[t._v("gclient sync")]),t._v(" 直到成功,大概近10个G")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("➜ engine gclient "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("sync")]),t._v("\nUpdating depot_tools"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".\nusing /var/folders/36/n74_k53x5h5fz70gkd3jr5p00000gn/T/goma_lxf as tmpdir\nINFO: creating cache "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("dir")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("/var/folders/36/n74_k53x5h5fz70gkd3jr5p00000gn/T/goma_lxf/goma_cache"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(".\ncompiler_proxy is not running\n\n________ running "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'git -c core.deltaBaseCacheLimit=2g clone --no-checkout --progress git@github.com:flutter/engine.git /Users/lxf/Desktop/LXF/engine/src/_gclient_flutter_t0dh3d3h'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'/Users/lxf/Desktop/LXF/engine'")]),t._v("\nCloning into "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'/Users/lxf/Desktop/LXF/engine/src/_gclient_flutter_t0dh3d3h'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".\nremote: Enumerating objects: "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("326233")]),t._v(", done.\nremote: Counting objects: "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("100")]),t._v("% "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("42")]),t._v("/42"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(", done.\nremote: Compressing objects: "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("100")]),t._v("% "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("39")]),t._v("/39"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(", done.\nremote: Total "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("326233")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("delta "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("11")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(", reused "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("8")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("delta "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(", pack-reused "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("326191")]),t._v("\nReceiving objects: "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("100")]),t._v("% "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("326233")]),t._v("/326233"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("394.55")]),t._v(" MiB "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("3.88")]),t._v(" MiB/s, done.\nResolving deltas: "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("100")]),t._v("% "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("242690")]),t._v("/242690"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(", done.\n"),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),s("span",{pre:!0,attrs:{class:"token operator"}},[s("span",{pre:!0,attrs:{class:"token file-descriptor important"}},[t._v("0")]),t._v(">")]),t._v("WARNING: subprocess "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('\'"git" "-c" "core.deltaBaseCacheLimit=2g" "fetch" "origin" "d40d15e994ed60d32bcfc9ab87004dfb028dfbd6" "--no-tags"\'')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" /Users/lxf/Desktop/LXF/engine/src/third_party/harfbuzz failed"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" will retry after a short nap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".\nSyncing projects: "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("100")]),t._v("% "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("134")]),t._v("/134"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(", done.\nHook "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'python3 src/flutter/tools/pub_get_offline.py'")]),t._v(" took "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("19.13")]),t._v(" secs\nRunning hooks: "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("100")]),t._v("% "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("11")]),t._v("/11"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(", done.\n➜ engine\n")])])]),s("p",[t._v("拉取完成后你可以进入 "),s("code",[t._v("src/flutter")]),t._v(" 查看当前下载下来的 "),s("code",[t._v("engine")]),t._v(" 的 "),s("code",[t._v("commit id")])]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("➜ "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("cd")]),t._v(" /Users/lxf/Desktop/LXF/engine/src/flutter\n➜ flutter git:"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("1837b5be5f"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("如果你的终端没有显示 "),s("code",[t._v("git:(commit id)")]),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("git")]),t._v(" rev-parse HEAD\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 1837b5be5f0f1376a1ccf383950e83a80177fb4e")]),t._v("\n")])])]),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("tools")]),t._v(" 目录,执行几条 "),s("code",[t._v("gn")]),t._v(" 命令")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("➜ "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("cd")]),t._v(" engine/src/flutter/tools\n")])])]),s("p",[t._v("生成 "),s("code",[t._v("iOS")]),t._v(" 设备端可执行文件所需的构建文件")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("➜ tools git:"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("1837b5be5f"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" ./gn "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--ios")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--unoptimized")]),t._v("\n\nGOMA usage was specified but can't be found, falling back to "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("local")]),t._v(" builds. Set the GOMA_DIR environment variable to fix GOMA.\nGenerating GN files in: out/ios_debug_unopt\nGenerating Xcode projects took 211ms\nGenerating compile_commands took 55ms\nDone. Made "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("801")]),t._v(" targets from "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("274")]),t._v(" files "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" 6165ms\n")])])]),s("p",[t._v("如果你想调试模拟器,则加上 "),s("code",[t._v("--simulator")])]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("gn "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--ios")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--simulator")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--unoptimized")]),t._v("\n")])])]),s("p",[t._v("生成主机端可执行文件所需的构建文件")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("➜ tools git:"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("1837b5be5f"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" ./gn "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--unoptimized")]),t._v("\n\nGOMA usage was specified but can't be found, falling back to "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("local")]),t._v(" builds. Set the GOMA_DIR environment variable to fix GOMA.\nUsing prebuilt Dart SDK binary. If you are editing Dart sources and wish to compile the Dart SDK, "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token variable"}},[s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("`")]),t._v("--no-prebuilt-dart-sdk"),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("`")])]),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v("\nGenerating GN files in: out/host_debug_unopt\nGenerating Xcode projects took 273ms\nGenerating compile_commands took 70ms\nDone. Made "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1110")]),t._v(" targets from "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("340")]),t._v(" files "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" 46800ms\n")])])]),s("p",[t._v("执行完成之后,在 "),s("code",[t._v("engine/src/out")]),t._v(" 目录下就会多出相应端的构建文件目录")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202303121745138.png",alt:""}})]),t._v(" "),s("p",[t._v("接下来使用 "),s("code",[t._v("Ninja")]),t._v(" 进行编译")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("ninja "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-C")]),t._v(" host_debug_unopt "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" ninja "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-C")]),t._v(" ios_debug_unopt\n")])])]),s("p",[t._v("如果你是用 "),s("code",[t._v("M1/M2")]),t._v(" 电脑去编译旧引擎源码,可能会在编译 "),s("code",[t._v("ios_debug_unopt")]),t._v(" 时遇到如下错误")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("ninja: Entering directory "),s("span",{pre:!0,attrs:{class:"token variable"}},[s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("`")]),t._v("host_debug_unopt'\nninja: no work to do.\nninja: Entering directory "),s("span",{pre:!0,attrs:{class:"token variable"}},[t._v("`")])]),t._v("ios_debug_unopt'\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("13")]),t._v("/369"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" ACTION //flutter/lib/snapshot:create_arm_gen_snapshot"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("//build/toolchain/mac:ios_clang_arm"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nFAILED: clang_x64/gen_snapshot_arm64\npython3 "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v("/"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v("/flutter/sky/tools/create_macos_gen_snapshots.py "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--dst")]),t._v(" /Users/lxf/Desktop/LXF/engine/src/out/ios_debug_unopt/clang_x64 --arm64-out-dir /Users/lxf/Desktop/LXF/engine/src/out/ios_debug_unopt\nCannot "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("find")]),t._v(" gen_snapshot at /Users/lxf/Desktop/LXF/engine/src/out/ios_debug_unopt/clang_x64/gen_snapshot\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("16")]),t._v("/369"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" LINK clang_arm64/impellerc\nninja: build stopped: subcommand failed.\n")])])]),s("p",[t._v("这时你只需要进入到 "),s("code",[t._v("engine/src/out/ios_debug_unopt")]),t._v(" 目录下,将 "),s("code",[t._v("clang_arm64")]),t._v(" 目录中的 "),s("code",[t._v("gen_snapshot")]),t._v(" 拷贝到 "),s("code",[t._v("clang_x64")]),t._v(",然后再继续执行 "),s("code",[t._v("ninja -C ios_debug_unopt")]),t._v(" 即可。")]),t._v(" "),s("p",[t._v("这里强调一下:")]),t._v(" "),s("ul",[s("li",[t._v("如果你修改了引擎的源代码,需要重新执行相应的 "),s("code",[t._v("ninja")]),t._v(" 命令,如调试引擎时我们会修改代码,如果不执行 "),s("code",[t._v("ninja")]),t._v(" 命令,你会发现断点修改的代码其它是旧代码!")]),t._v(" "),s("li",[t._v("如果是新增或删除文件,则需要先执行 "),s("code",[t._v("gn")]),t._v(" 命令,再执行 "),s("code",[t._v("ninja")]),t._v(" 命令。")]),t._v(" "),s("li",[t._v("我建议直接 "),s("code",[t._v("gn")]),t._v(" 和 "),s("code",[t._v("ninja")]),t._v(" 命令一起执行,不用管什么情况,脚本会自己检查是否有文件变更,以及是否需要重新生成相应的文件的。")])]),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("Flutter")]),t._v(" 项目,"),s("code",[t._v("IDE")]),t._v(" 以 "),s("code",[t._v("VSCode")]),t._v(" 为例,添加 "),s("code",[t._v("--local-engine-src-path")]),t._v(" 和 "),s("code",[t._v("--local-engine")]),t._v(" 两个参数,完整配置如下")]),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('"name"')]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"flutter_demo"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token property"}},[t._v('"request"')]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"launch"')]),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('"dart"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token property"}},[t._v('"args"')]),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 string"}},[t._v('"--local-engine-src-path"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/LXF/engine/src"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"--local-engine"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"ios_debug_unopt"')]),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("table",[s("thead",[s("tr",[s("th",[t._v("参数")]),t._v(" "),s("th",[t._v("值")])])]),t._v(" "),s("tbody",[s("tr",[s("td",[s("code",[t._v("--local-engine-src-path")])]),t._v(" "),s("td",[t._v("固定为引擎的 "),s("code",[t._v("src")]),t._v(" 路径")])]),t._v(" "),s("tr",[s("td",[s("code",[t._v("--local-engine")])]),t._v(" "),s("td",[t._v("真机: "),s("code",[t._v("ios_debug_unopt")]),s("br"),t._v("模拟器: "),s("code",[t._v("ios_debug_sim_unopt")])])])])]),t._v(" "),s("p",[t._v("按 "),s("code",[t._v("F5")]),t._v(" 跑一遍就可以使用指定引擎运行 "),s("code",[t._v("Flutter")]),t._v(" 项目了。")]),t._v(" "),s("p",[t._v("细心的你可能会发现在 "),s("code",[t._v("flutter_demo/ios/Flutter/Generated.xcconfig")]),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 punctuation"}},[t._v("..")]),t._v(".\n"),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("FLUTTER_ENGINE")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("/Users/lxf/Desktop/LXF/engine/src\n"),s("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("LOCAL_ENGINE")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("ios_debug_unopt\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".\n")])])]),s("p",[t._v("如果我们要调试引擎代码,请打开你的 "),s("code",[t._v("Runner.xcworkspace")]),t._v(",然后将 "),s("code",[t._v("flutter_engine")]),t._v(" 拖入项目")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202303121745765.png",alt:""}})]),t._v(" "),s("p",[t._v("找到 "),s("code",[t._v("FlutterViewController.mm")]),t._v(" 给 "),s("code",[t._v("touchesBegan")]),t._v(" 打个断点,再运行项目,点击屏幕就会进入到断点处,现在就可以开始愉快的调试 "),s("code",[t._v("flutter_engine")]),t._v(" 了")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202303121745762.png",alt:""}})]),t._v(" "),s("p",[t._v("如果你的断点不生效,那请先检查 "),s("code",[t._v("Generated.xcconfig")]),t._v(" 里的 "),s("code",[t._v("FLUTTER_ENGINE")]),t._v(" 和 "),s("code",[t._v("LOCAL_ENGINE")]),t._v(" 变量。")]),t._v(" "),s("p",[t._v("到这里,引擎的编译调试就介绍完毕了。")]),t._v(" "),s("p",[t._v("下一篇,我们来详细说说怎么给 "),s("code",[t._v("Flutter")]),t._v(" 引擎提交 "),s("code",[t._v("PR")]),t._v(" 🤠")]),t._v(" "),s("h2",{attrs:{id:"六、资料"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#六、资料"}},[t._v("#")]),t._v(" 六、资料")]),t._v(" "),s("ul",[s("li",[s("a",{attrs:{href:"https://github.com/flutter/flutter/wiki/Setting-up-the-Engine-development-environment",target:"_blank",rel:"noopener noreferrer"}},[t._v("Setting up the Engine development environment"),s("OutboundLink")],1)]),t._v(" "),s("li",[s("a",{attrs:{href:"https://github.com/flutter/flutter/wiki/Compiling-the-engine#compiling-for-ios-from-macos",target:"_blank",rel:"noopener noreferrer"}},[t._v("Compiling the engine"),s("OutboundLink")],1)])])])}),[],!1,null,null,null);s.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/129.1c49caac.js b/assets/js/129.1c49caac.js new file mode 100644 index 000000000..0741f68a7 --- /dev/null +++ b/assets/js/129.1c49caac.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[129],{442:function(t,s,a){"use strict";a.r(s);var e=a(8),n=Object(e.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("p",[t._v("在上一篇 "),s("RouterLink",{attrs:{to:"/pages/6caa6f/"}},[t._v("Flutter - 引擎调试(iOS篇)🛠")]),t._v(" 中,我们已经知道了如何去编译调试引擎,今天就来说一说我从调试到提交 "),s("code",[t._v("PR")]),t._v(" 的过程吧。")],1),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("Flutter")]),t._v(" 官方仓库发现了别人提的一个 "),s("code",[t._v("issue")]),t._v(",是关于苹果移动光标的特性功能,先给大家看看该功能的官方演示吧")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202304020948522.gif",alt:""}})]),t._v(" "),s("p",[s("code",[t._v("issue")]),t._v(" 链接: "),s("a",{attrs:{href:"https://github.com/flutter/flutter/issues/122139",target:"_blank",rel:"noopener noreferrer"}},[t._v("[iOS] Hold and drag spacebar does not move cursor when obscureText is true."),s("OutboundLink")],1)]),t._v(" "),s("p",[t._v("问题复现的动图如下:")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202304020949821.gif",alt:""}})]),t._v(" "),s("p",[s("code",[t._v("issue")]),t._v(" 问题总结:")]),t._v(" "),s("p",[s("code",[t._v("Flutter")]),t._v(" 的 "),s("code",[t._v("TextFormField")]),t._v(" 或 "),s("code",[t._v("TextField")]),t._v(" ,在"),s("code",[t._v("obscureText")]),t._v(" 的初始值和切换值的这两种情况下,长按空格并拖拽时可移动光标这一特性,出现了有别于原生控件的表现,具体如下:")]),t._v(" "),s("table",[s("thead",[s("tr",[s("th",[t._v("    行为"),s("br"),t._v("端")]),t._v(" "),s("th",[t._v("获得焦点"),s("br"),t._v("obscureText=false"),s("br"),t._v("(当前状态: 明文)")]),t._v(" "),s("th",[t._v("切换"),s("br"),t._v("obscureText=true"),s("br"),t._v("(当前状态: 密文)")])])]),t._v(" "),s("tbody",[s("tr",[s("td",[t._v("Flutter 的 TextField")]),t._v(" "),s("td",[t._v("可用")]),t._v(" "),s("td",[s("code",[t._v("可用")])])]),t._v(" "),s("tr",[s("td",[t._v("原生的 UITextField")]),t._v(" "),s("td",[t._v("可用")]),t._v(" "),s("td",[t._v("不可用")])])])]),t._v(" "),s("table",[s("thead",[s("tr",[s("th",[t._v("    行为"),s("br"),t._v("端")]),t._v(" "),s("th",[t._v("获得焦点"),s("br"),t._v("obscureText=true"),s("br"),t._v("(当前状态: 密文)")]),t._v(" "),s("th",[t._v("切换"),s("br"),t._v("obscureText=false"),s("br"),t._v("(当前状态: 明文)")])])]),t._v(" "),s("tbody",[s("tr",[s("td",[t._v("Flutter 的 TextField")]),t._v(" "),s("td",[t._v("不可用")]),t._v(" "),s("td",[s("code",[t._v("不可用")])])]),t._v(" "),s("tr",[s("td",[t._v("原生的 UITextField")]),t._v(" "),s("td",[t._v("不可用")]),t._v(" "),s("td",[t._v("可用")])])])]),t._v(" "),s("p",[t._v("可以看到,正常的表现应该是只要是明文,则该功能就可用,而密文时则该功能不可用,但是此时在 "),s("code",[t._v("Flutter")]),t._v(" 端,该功能可不可用取决于获取焦点时是否为明文!")]),t._v(" "),s("p",[t._v("这里贴出 "),s("code",[t._v("issue")]),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-literal"}},[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("MyHomePage")]),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 keyword"}},[t._v("const")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MyHomePage")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Key")]),s("span",{pre:!0,attrs:{class:"token operator"}},[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(" "),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 function"}},[t._v("@override")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("State")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MyHomePage")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),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("_MyHomePageState")]),s("span",{pre:!0,attrs:{class:"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(" _MyHomePageState "),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 generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MyHomePage")]),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(" _controller "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextEditingController")]),s("span",{pre:!0,attrs:{class:"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 bool _obscureText "),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\n "),s("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@override")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),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 class-name"}},[t._v("BuildContext")]),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 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 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("const")]),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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Flutter Demo'")])]),s("span",{pre:!0,attrs:{class:"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 class-name"}},[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(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[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("20")]),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 class-name"}},[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 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 class-name"}},[t._v("SizedBox")]),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(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("80")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("TextFormField")]),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(" _controller"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n obscureText"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" _obscureText"),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 class-name"}},[t._v("InputDecoration")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n suffixIcon"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("IconButton")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n icon"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),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("\n _obscureText "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Icons")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("visibility_off "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Icons")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("visibility"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 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 _obscureText "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("_obscureText"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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(")")]),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("接下来我们一起去探索一下引擎源码,找到出现问题的根本原因。")]),t._v(" "),s("h2",{attrs:{id:"二、探索"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#二、探索"}},[t._v("#")]),t._v(" 二、探索")]),t._v(" "),s("h3",{attrs:{id:"_1、引擎准备"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、引擎准备"}},[t._v("#")]),t._v(" 1、引擎准备")]),t._v(" "),s("p",[t._v("配置 "),s("code",[t._v(".gclient")]),t._v(",将 "),s("code",[t._v("url")]),t._v(" 改成你自己 "),s("code",[t._v("fork")]),t._v(" 的仓库的 "),s("code",[t._v("git")]),t._v(" 地址")]),t._v(" "),s("blockquote",[s("p",[t._v("这样方便我们后续调试完成后,直接将修改的代码提交至自己仓库,顺带提 "),s("code",[t._v("PR")])])]),t._v(" "),s("div",{staticClass:"language-json extra-class"},[s("pre",{pre:!0,attrs:{class:"language-json"}},[s("code",[t._v("solutions = "),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 property"}},[t._v('"url"')]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"git@github.com:LinXunFeng/engine.git@dd91363757dd2e8e4dc22d3be62741d461ceb005"')]),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(",")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n")])])]),s("p",[t._v("关联调试源码的操作可在上一篇 "),s("RouterLink",{attrs:{to:"/pages/6caa6f/"}},[t._v("Flutter - 引擎调试(iOS篇)🛠")]),t._v(" 中查看,加上篇幅限制,所以这里就不再赘述,本文重点是解决 "),s("code",[t._v("issue")]),t._v(" 提及的问题。")],1),t._v(" "),s("h3",{attrs:{id:"_2、查找病因"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、查找病因"}},[t._v("#")]),t._v(" 2、查找病因")]),t._v(" "),s("p",[t._v("在引擎中,与 "),s("code",[t._v("TextField")]),t._v(" 相关的类为 "),s("code",[t._v("FlutterTextInputPlugin")]),t._v(",在该类的所在文件中找到 "),s("code",[t._v("obscureText")]),t._v(" 定义的常量 "),s("code",[t._v("kSecureTextEntry")])]),t._v(" "),s("div",{staticClass:"language-objc extra-class"},[s("pre",{pre:!0,attrs:{class:"language-objc"}},[s("code",[s("span",{pre:!0,attrs:{class:"token macro property"}},[s("span",{pre:!0,attrs:{class:"token directive-hash"}},[t._v("#")]),s("span",{pre:!0,attrs:{class:"token directive keyword"}},[t._v("pragma")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("mark "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v(" TextInputConfiguration Field Names")])]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("static")]),t._v(" NSString"),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(" kSecureTextEntry "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('@"obscureText"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("p",[t._v("搜索 "),s("code",[t._v("kSecureTextEntry")]),t._v(",发现了除 "),s("code",[t._v("init")]),t._v(" 方法之外,仅在方法 "),s("code",[t._v("configureWithDictionary")]),t._v(" 中以 "),s("code",[t._v("kSecureTextEntry")]),t._v(" 做为 "),s("code",[t._v("key")]),t._v(" 向 "),s("code",[t._v("configuration")]),t._v(" 取值并将值赋予 "),s("code",[t._v("secureTextEntry")]),t._v(" 属性")]),t._v(" "),s("div",{staticClass:"language-objc extra-class"},[s("pre",{pre:!0,attrs:{class:"language-objc"}},[s("code",[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("void")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("configureWithDictionary"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("NSDictionary"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("configuration "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("secureTextEntry "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("configuration"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("kSecureTextEntry"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" boolValue"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("secureTextEntry")]),t._v(" 必定是在哪里有被使用到。")]),t._v(" "),s("p",[t._v("如果我们把 "),s("code",[t._v("secureTextEntry")]),t._v(" 相关的代码注释个遍去排查,这无异于大海捞针,该方式也过于笨拙,且明确告诉你徒劳无功,那怎么办呢?🤔")]),t._v(" "),s("p",[t._v("是的,重写它的 "),s("code",[t._v("getter")]),t._v(" 方法,并打个断点")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202304020949382.png",alt:""}})]),t._v(" "),s("p",[t._v("这里可别忘了先编译!")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("ninja "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-C")]),t._v(" ios_debug_unopt\n")])])]),s("p",[t._v("点击输入框获取焦点,"),s("code",[t._v("Xcode")]),t._v(" 来到断点处,注意看左边的调用栈")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202304020949907.png",alt:""}})]),t._v(" "),s("p",[t._v("发现 "),s("code",[t._v("isSecureTextEntry")]),t._v(" 是被系统调用的!")]),t._v(" "),s("p",[t._v("调用栈向下找,发现是 "),s("code",[t._v("_activeView")]),t._v(" 成为第一响应者后间接触发的")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202304020950488.png",alt:""}})]),t._v(" "),s("p",[s("code",[t._v("activeView")]),t._v(" 的声明:")]),t._v(" "),s("div",{staticClass:"language-objc extra-class"},[s("pre",{pre:!0,attrs:{class:"language-objc"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("@interface")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("FlutterTextInputPlugin")]),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 keyword"}},[t._v("@property")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("nonatomic"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" retain"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" FlutterTextInputView"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" activeView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("@end")]),t._v("\n")])])]),s("p",[s("code",[t._v("FlutterTextInputView")]),t._v(" 的声明:")]),t._v(" "),s("div",{staticClass:"language-objc extra-class"},[s("pre",{pre:!0,attrs:{class:"language-objc"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("@interface")]),t._v(" FlutterTextInputView "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" UIView "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("UITextInput"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" UIScribbleInteractionDelegate"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// UITextInputTraits")]),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("@property")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("nonatomic"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" getter"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("isSecureTextEntry"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" BOOL secureTextEntry"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("UITextInput")]),t._v(" -> "),s("code",[t._v("UIKeyInput")]),t._v(" -> "),s("code",[t._v("UITextInputTraits")])]),t._v(" "),s("div",{staticClass:"language-objc extra-class"},[s("pre",{pre:!0,attrs:{class:"language-objc"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("@protocol")]),t._v(" UITextInput "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("UIKeyInput"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._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("@end")]),t._v("\n\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("@protocol")]),t._v(" UIKeyInput "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("UITextInputTraits"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._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("@end")]),t._v("\n\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("@protocol")]),t._v(" UITextInputTraits "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("NSObject"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._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("@property")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("nonatomic"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("getter"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("isSecureTextEntry"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" BOOL secureTextEntry"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// default is NO")]),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("@end")]),t._v("\n")])])]),s("p",[t._v("可以发现 "),s("code",[t._v("FlutterTextInputView")]),t._v(" 最终遵守了 "),s("code",[t._v("UITextInputTraits")]),t._v(" 协议,该协议声明了 "),s("code",[t._v("secureTextEntry")]),t._v(" 属性,而该属性不仅决定着原生控件显示明文或密文,还控制着键盘是否可以长按移动光标。")]),t._v(" "),s("p",[t._v("现在来看一下 "),s("code",[t._v("FlutterTextInputPlugin.mm")]),t._v(" 文件中的 "),s("code",[t._v("handleMethodCall")]),t._v(" 方法,其是处理 "),s("code",[t._v("Flutter")]),t._v(" 与引擎交互的 "),s("code",[t._v("method channel")]),t._v(" 的方法")]),t._v(" "),s("div",{staticClass:"language-objc extra-class"},[s("pre",{pre:!0,attrs:{class:"language-objc"}},[s("code",[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("void")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("handleMethodCall"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("FlutterMethodCall"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("call result"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("FlutterResult"),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 NSString"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" method "),s("span",{pre:!0,attrs:{class:"token operator"}},[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(";")]),t._v("\n id args "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" call"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("arguments"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("method isEqualToString"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("kHideMethod"),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 comment"}},[t._v("// TextInput.hide")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),t._v(" hideTextInput"),s("span",{pre:!0,attrs:{class:"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("result")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("nil"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("method isEqualToString"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("kSetClientMethod"),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 comment"}},[t._v("// TextInput.setClient")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),t._v(" setTextInputClient"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("args"),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("]")]),t._v(" intValue"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" withConfiguration"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("args"),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 function"}},[t._v("result")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("nil"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 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",[s("code",[t._v("TextInput.setClient")]),t._v(": 这是在输入框获取焦点的时候与原生控件建立事务时传递的方法名,与之相对应的是 "),s("code",[t._v("setTextInputClient")]),t._v(" 方法,在该方法内创建新的 "),s("code",[t._v("FlutterTextInputView")]),t._v(" 控件,并赋值给 "),s("code",[t._v("activeView")])]),t._v(" "),s("div",{staticClass:"language-objc extra-class"},[s("pre",{pre:!0,attrs:{class:"language-objc"}},[s("code",[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("void")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("setTextInputClient"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("int")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("client withConfiguration"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("NSDictionary"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("configuration "),s("span",{pre:!0,attrs:{class:"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("self")]),t._v(" resetAllClientIds"),s("span",{pre:!0,attrs:{class:"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("// Hide all input views from autofill, only make those in the new configuration visible")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// to autofill.")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),t._v(" changeInputViewsAutofillVisibility"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("NO"),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("// Update the current active view.")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("switch")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("AutofillTypeOf")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("configuration"),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("case")]),t._v(" kFlutterAutofillTypeNone"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("activeView "),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("self")]),t._v(" createInputViewWith"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("configuration"),s("span",{pre:!0,attrs:{class:"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("break")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" kFlutterAutofillTypeRegular"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// If the group does not involve password autofill, only install the")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// input view that's being focused.")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("activeView "),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("self")]),t._v(" updateAndShowAutofillViews"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("nil\n focusedField"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("configuration\n isPasswordRelated"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("NO"),s("span",{pre:!0,attrs:{class:"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("break")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" kFlutterAutofillTypePassword"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("activeView "),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("self")]),t._v(" updateAndShowAutofillViews"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("configuration"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("kAssociatedAutofillFields"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n focusedField"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("configuration\n isPasswordRelated"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("YES"),s("span",{pre:!0,attrs:{class:"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("break")]),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("_activeView setTextInputClient"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("client"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("_activeView reloadInputViews"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 keyword"}},[t._v("self")]),t._v(" cleanUpViewHierarchy"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("NO clearText"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("YES delayRemoval"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("YES"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("addToInputParentViewIfNeeded")]),t._v(" 方法将 "),s("code",[t._v("activeView")]),t._v(" 视图添加到 "),s("code",[t._v("inputHider")]),t._v(" 视图上。")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202304020950215.png",alt:""}})]),t._v(" "),s("p",[s("code",[t._v("inputHider")]),t._v(" 视图也在该方法中被添加到 "),s("code",[t._v("_viewController.view")]),t._v(" 上")]),t._v(" "),s("div",{staticClass:"language-objc extra-class"},[s("pre",{pre:!0,attrs:{class:"language-objc"}},[s("code",[s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("UIView"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("hostView "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n UIView"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" host "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" _viewController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("view"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("NSAssert")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("host "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" nullptr"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('@"The application must have a host view since the keyboard client "')]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('@"must be part of the responder chain to function. The host view controller is %@"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n _viewController"),s("span",{pre:!0,attrs:{class:"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(" host"),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("TextInput.hide")]),t._v(": 这是当输入框失去焦点的时候传递的方法名,用于移除当前的 "),s("code",[t._v("activeView")]),t._v(" 视图 和 "),s("code",[t._v("_inputHider")]),t._v(" 视图,对应的方法如下:")]),t._v(" "),s("div",{staticClass:"language-objc extra-class"},[s("pre",{pre:!0,attrs:{class:"language-objc"}},[s("code",[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("void")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("hideTextInput "),s("span",{pre:!0,attrs:{class:"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("self")]),t._v(" removeEnableFlutterTextInputViewAccessibilityTimer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n _activeView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("accessibilityEnabled "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" NO"),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("_activeView resignFirstResponder"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("_activeView removeFromSuperview"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("_inputHider removeFromSuperview"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("Flutter")]),t._v(" 的 "),s("code",[t._v("TextField")]),t._v(" 在每一次获取焦点时,引擎端会创建一个相应的 "),s("code",[t._v("FlutterTextInputView")]),t._v(" 原生控件与之对应,并使用 "),s("code",[t._v("activeView")]),t._v(" 来记录当前获取了焦点的那个控件,当失去焦点时,相应的控件会被移除和销毁。")])]),t._v(" "),s("p",[t._v("断点放行后点击眼睛按钮,将输入框切换为密文,你会发现 "),s("code",[t._v("isSecureTextEntry")]),t._v(" 的 "),s("code",[t._v("getter")]),t._v(" 方法并不会被调用,这就是病因,此时虽然从 "),s("code",[t._v("Flutter")]),t._v(" 的 "),s("code",[t._v("TextField")]),t._v(" 的显示来看确实是有在进行明文与密文的切换,但是这只是 "),s("code",[t._v("Flutter")]),t._v(" 处理后的显示结果,而在引擎端的 "),s("code",[t._v("activeView")]),t._v(" 的 "),s("code",[t._v("secureTextEntry")]),t._v(" 还是一开始的值,所以此时长按移动光标的功能还是生效的。")]),t._v(" "),s("p",[t._v("因此,要解决这个问题就得让 "),s("code",[t._v("activeView")]),t._v(" 的 "),s("code",[t._v("secureTextEntry")]),t._v(" 得到更新,并且调用 "),s("code",[t._v("reloadInputViews")]),t._v(" 重新绘制键盘。")]),t._v(" "),s("h2",{attrs:{id:"三、动手解决"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#三、动手解决"}},[t._v("#")]),t._v(" 三、动手解决")]),t._v(" "),s("blockquote",[s("p",[t._v("注: "),s("code",[t._v("TextInput.updateConfig")]),t._v(" 该常量在 "),s("code",[t._v("Flutter")]),t._v(" 源码里是早就存在的,但是仅在引擎的 "),s("code",[t._v("Web")]),t._v(" 端使用到,引擎的 "),s("code",[t._v("iOS")]),t._v(" 端则没有使用。")])]),t._v(" "),s("h3",{attrs:{id:"_1、engine-仓库"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、engine-仓库"}},[t._v("#")]),t._v(" 1、"),s("code",[t._v("engine")]),t._v(" 仓库")]),t._v(" "),s("p",[s("code",[t._v("FlutterTextInputPlugin.mm")])]),t._v(" "),s("p",[t._v("新增 "),s("code",[t._v("TextInput.updateConfig")]),t._v(" 常量 "),s("code",[t._v("kUpdateConfigMethod")])]),t._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(' static NSString* const kSetSelectionRectsMethod = @"Scribble.setSelectionRects";\n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(' static NSString* const kStartLiveTextInputMethod = @"TextInput.startLiveTextInput";\n')])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(' static NSString* const kUpdateConfigMethod = @"TextInput.updateConfig";\n')])])])])]),s("p",[t._v("补充处理 "),s("code",[t._v("TextInput.updateConfig")]),t._v(" 的方法 "),s("code",[t._v("updateConfig")])]),t._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v("- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" NSString* method = call.method;\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" id args = call.arguments;\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" if ([method isEqualToString:kShowMethod]) {\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" ...\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" } else if ([method isEqualToString:kStartLiveTextInputMethod]) {\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" [self startLiveTextInput];\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" result(nil);\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" } else if ([method isEqualToString:kUpdateConfigMethod]) {\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" [self updateConfig:args];\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" result(nil);\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" } else {\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" result(FlutterMethodNotImplemented);\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" }\n")])])])])]),s("p",[t._v("更新 "),s("code",[t._v("secureTextEntry")]),t._v(" 并调用 "),s("code",[t._v("reloadInputViews")]),t._v(" 方法")]),t._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" - (void)updateConfig:(NSDictionary*)dictionary {\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" BOOL isSecureTextEntry = [dictionary[kSecureTextEntry] boolValue];\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" for (UIView* view in self.textInputViews) {\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" if ([view isKindOfClass:[FlutterTextInputView class]]) {\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" FlutterTextInputView* inputView = (FlutterTextInputView*)view;\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // The feature of holding and draging spacebar to move cursor is affected by\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // secureTextEntry, so when obscureText is updated, we need to update secureTextEntry\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // and call reloadInputViews.\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // https://github.com/flutter/flutter/issues/122139\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" if (inputView.isSecureTextEntry != isSecureTextEntry) {\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" inputView.secureTextEntry = isSecureTextEntry;\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" [inputView reloadInputViews];\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" }\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" }\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" }\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" }\n")])])])])]),s("h3",{attrs:{id:"_2、flutter-仓库"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、flutter-仓库"}},[t._v("#")]),t._v(" 2、"),s("code",[t._v("flutter")]),t._v(" 仓库")]),t._v(" "),s("p",[s("code",[t._v("editable_text.dart")])]),t._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" @override\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" void didUpdateWidget(EditableText oldWidget) {\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" super.didUpdateWidget(oldWidget);\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" ...\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" \n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" if (_hasInputConnection) {\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" if (oldWidget.obscureText != widget.obscureText) {\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" _textInputConnection!.updateConfig(_effectiveAutofillClient.textInputConfiguration);\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" }\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" }\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" ...\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" }\n")])])])])]),s("p",[t._v("这样就修复好了,别忘了先执行 "),s("code",[t._v("ninja -C ios_debug_unopt")]),t._v(" 进行编译,然后再进行调试验证。")]),t._v(" "),s("p",[t._v("经过调试,一切正常,接下来就是提交 "),s("code",[t._v("PR")]),t._v(" 了吗?不,要先添加单元测试,不过具体的单测代码不是重点,所以这里只说明如何去跑单测。")]),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("engine/src/out")]),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 comment"}},[t._v("# engine/src/flutter/tools")]),t._v("\n\n./gn "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--unoptimized")]),t._v("\n./gn "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--ios")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--unoptimized")]),t._v("\n./gn "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--ios")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--simulator")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--unoptimized")]),t._v("\n")])])]),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# engine/src/out")]),t._v("\n\nninja "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-C")]),t._v(" host_debug_unopt "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v("\nninja "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-C")]),t._v(" ios_debug_unopt "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v("\nninja "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-C")]),t._v(" ios_debug_sim_unopt\n")])])]),s("h3",{attrs:{id:"_1、脚本跑单测"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、脚本跑单测"}},[t._v("#")]),t._v(" 1、脚本跑单测")]),t._v(" "),s("blockquote",[s("p",[t._v("注: 请提前打开 "),s("code",[t._v("iOS")]),t._v(" 模拟器,执行 "),s("code",[t._v("run_tests.py")]),t._v(" 跑单元测试时需要用到")])]),t._v(" "),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 comment"}},[t._v("# engine/src/flutter/testing")]),t._v("\n\n./run_tests.py "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--type")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("objc\n")])])]),s("p",[t._v("如果报错了,则按其提示先执行相应的命令,完成后再继续执行 "),s("code",[t._v("run_tests.py")])]),t._v(" "),s("p",[t._v("比如这是我执行脚本后的报错内容")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("➜ testing git:"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("main"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" ./run_tests.py "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--type")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("objc\nTraceback "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("most recent call last"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(":\n File "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/LXF/engine/src/flutter/testing/./run_tests.py"')]),t._v(", line "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1150")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("module"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n sys.exit"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),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("\n File "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/LXF/engine/src/flutter/testing/./run_tests.py"')]),t._v(", line "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1136")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" main\n run_objc_tests"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("args.ios_variant, args.objc_filter"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n File "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/LXF/engine/src/flutter/testing/./run_tests.py"')]),t._v(", line "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("682")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" run_objc_tests\n ensure_ios_tests_are_built"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ios_out_dir"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n File "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/lxf/Desktop/LXF/engine/src/flutter/testing/./run_tests.py"')]),t._v(", line "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("585")]),t._v(", "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" ensure_ios_tests_are_built\n assert os.path.exists"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("tmp_out_dir\nAssertionError: /Users/lxf/Desktop/LXF/engine/src/out/ios_debug_sim_unopt or /Users/lxf/Desktop/LXF/engine/src/out/ios_debug_sim_unopt/libios_test_flutter.dylib doesn't exist. Please run the following commands:\ngn "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--ios")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--unoptimized")]),t._v(" --runtime-mode"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("debug --no-lto "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--simulator")]),t._v("\nautoninja "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-C")]),t._v(" /Users/lxf/Desktop/LXF/engine/src/out/ios_debug_sim_unopt ios_test_flutter\n")])])]),s("p",[t._v("其实主要原因看这句提示:")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("engine/src/out/ios_debug_sim_unopt/libios_test_flutter.dylib doesn't exist\n")])])]),s("p",[t._v("所以我们需要进入到 "),s("code",[t._v("engine/src/out")]),t._v(" 目录下执行如下命令即可")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("autoninja "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-C")]),t._v(" /Users/lxf/Desktop/LXF/engine/src/out/ios_debug_sim_unopt ios_test_flutter\n")])])]),s("p",[t._v("执行完 "),s("code",[t._v("run_tests.py")]),t._v(" 会自动打开名为 "),s("code",[t._v("IosUnitTestsSimulator")]),t._v(" 的模拟器(该模拟器是以 "),s("code",[t._v("iPhone 11")]),t._v(" 为原型创建的),然后终端会跟随打印单测的执行日志")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("\n** TEST SUCCEEDED **\n\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v(".\n\nCommand run successfully "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0.46")]),t._v(" seconds: xcrun simctl delete IosUnitTestsSimulator\n")])])]),s("p",[t._v("最终输出 "),s("code",[t._v("TEST SUCCEEDED")]),t._v(" 表示单测全部通过。")]),t._v(" "),s("h3",{attrs:{id:"_2、xcode-跑单测"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、xcode-跑单测"}},[t._v("#")]),t._v(" 2、"),s("code",[t._v("Xcode")]),t._v(" 跑单测")]),t._v(" "),s("p",[t._v("接下来介绍一下 "),s("code",[t._v("Xcode")]),t._v(" 跑单元测试,我们用 "),s("code",[t._v("Xcode")]),t._v(" 打开 "),s("code",[t._v("engine/src/flutter/testing/ios/IosUnitTests/IosUnitTests.xcodeproj")])]),t._v(" "),s("p",[t._v("打开后你会发现单测只有 "),s("code",[t._v("FlutterChannelsTest")])]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202304020950833.png",alt:""}})]),t._v(" "),s("p",[t._v("别急,按 "),s("code",[t._v("cmd + u")]),t._v(" 跑一遍就全出来了")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202304020951832.png",alt:""}})]),t._v(" "),s("p",[t._v("没错,细心的你会发现截图里 "),s("code",[t._v("SemanticsObjectTest")]),t._v(" 单测失败了,按官方文档的说明,我们需要选择 "),s("code",[t._v("iPhone 11")]),t._v(" 模拟器进行单测。")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202304020951260.png",alt:""}})]),t._v(" "),s("p",[t._v("主要原因是下面这行代码过不了,算出来的值不相等")]),t._v(" "),s("div",{staticClass:"language-objc extra-class"},[s("pre",{pre:!0,attrs:{class:"language-objc"}},[s("code",[s("span",{pre:!0,attrs:{class:"token function"}},[t._v("XCTAssertTrue")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("CGRectEqualToRect")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n scrollView"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("frame"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" \n "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("CGRectMake")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n x "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" effectivelyScale"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" effectivelyScale"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n w "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" effectivelyScale"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" h "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" effectivelyScale\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("p",[t._v("运行在 "),s("code",[t._v("iPhone 11")]),t._v(" 上的数据")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("scrollView.frame = (2.5 2.5; 25 50)\n\nfloat x = 10;\nfloat y = 10;\nfloat w = 100;\nfloat h = 200;\nfloat effectivelyScale = transformScale / screenScale; // 2.5\n")])])]),s("p",[t._v("运行在 "),s("code",[t._v("iPhone 14 Pro")]),t._v(" 上的数据")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("scrollView.frame = (1.66667 1.66667; 16.6667 33.3333)\n\nfloat x = 10;\nfloat y = 10;\nfloat w = 100;\nfloat h = 200;\nfloat effectivelyScale = transformScale / screenScale; // 0.166666672\n")])])]),s("p",[t._v("这也是为什么 "),s("code",[t._v("run_tests.py")]),t._v(" 脚本是以 "),s("code",[t._v("iPhone 11")]),t._v(" 为原型创建的 "),s("code",[t._v("IosUnitTestsSimulator")]),t._v(" 模拟器。")]),t._v(" "),s("p",[t._v("回归正题,在每一次调整完测试代码后,需要重新执行如下命令重新生成 "),s("code",[t._v("ios_test_flutter.dylib")]),t._v(",否则你的调整不会生效!")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("autoninja "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-C")]),t._v(" /Users/lxf/Desktop/LXF/engine/src/out/ios_debug_sim_unopt ios_test_flutter\n")])])]),s("p",[t._v("执行完再 "),s("code",[t._v("cmd + u")]),t._v(" 或者单独执行某一条单测。")]),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("format.sh")]),t._v(" 进行检查,确保没问题后再提交,有问题则根据提示处理一下代码。")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("engine/src/flutter/ci/format.sh\n")])])]),s("p",[t._v("好了,到此所有该做的事情都已经做完了,可以打开自己的仓库,点击 "),s("code",[t._v("Contribute")]),t._v(" -> "),s("code",[t._v("Open pull request")]),t._v(" 去提交 "),s("code",[t._v("PR")]),t._v(" 了。")]),t._v(" "),s("p",[t._v("这里放上我的相关 "),s("code",[t._v("PR")]),t._v(" 链接:")]),t._v(" "),s("ul",[s("li",[s("a",{attrs:{href:"https://github.com/flutter/engine/pull/40216",target:"_blank",rel:"noopener noreferrer"}},[t._v("引擎 PR"),s("OutboundLink")],1)]),t._v(" "),s("li",[s("a",{attrs:{href:"https://github.com/flutter/flutter/pull/122383",target:"_blank",rel:"noopener noreferrer"}},[t._v("Flutter PR"),s("OutboundLink")],1)])]),t._v(" "),s("blockquote",[s("p",[s("code",[t._v("PR")]),t._v(" 提交时间于 "),s("code",[t._v("2023.03.10")]),t._v(",文章完成时间为 "),s("code",[t._v("2023.04.01")]),t._v("。")])]),t._v(" "),s("p",[t._v("由于我的 "),s("code",[t._v("PR")]),t._v(" 与 "),s("code",[t._v("Flutter")]),t._v(" 成员目前做的一些修复在代码上有重叠的地方,所以 "),s("code",[t._v("PR")]),t._v(" 是否会被合并目前尚未可知。该篇主要记录了结合引擎去调试 "),s("code",[t._v("flutter bug")]),t._v(" 到解决和提交代码的整体流程,供大家参考与相互交流。")]),t._v(" "),s("h2",{attrs:{id:"六、资料"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#六、资料"}},[t._v("#")]),t._v(" 六、资料")]),t._v(" "),s("ul",[s("li",[s("a",{attrs:{href:"https://github.com/flutter/flutter/wiki/Testing-the-engine",target:"_blank",rel:"noopener noreferrer"}},[t._v("Testing the engine"),s("OutboundLink")],1)]),t._v(" "),s("li",[s("a",{attrs:{href:"https://github.com/flutter/engine/blob/master/testing/ios/IosUnitTests/README.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("IosUnitTests/README.md"),s("OutboundLink")],1)]),t._v(" "),s("li",[s("a",{attrs:{href:"https://api.flutter.dev/flutter/services/SystemChannels/textInput-constant.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("textInput constant"),s("OutboundLink")],1)])])])}),[],!1,null,null,null);s.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/13.0f67029d.js b/assets/js/13.0f67029d.js new file mode 100644 index 000000000..859ac2350 --- /dev/null +++ b/assets/js/13.0f67029d.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[13],{327:function(t,n,s){"use strict";s.r(n);var e=s(8),o=Object(e.a)({},(function(){return(0,this._self._c)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);n.default=o.exports}}]); \ No newline at end of file diff --git a/assets/js/130.b277ccc3.js b/assets/js/130.b277ccc3.js new file mode 100644 index 000000000..da96b3796 --- /dev/null +++ b/assets/js/130.b277ccc3.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[130],{441:function(t,s,a){"use strict";a.r(s);var e=a(8),n=Object(e.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h2",{attrs:{id:"一、概述"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#一、概述"}},[t._v("#")]),t._v(" 一、概述")]),t._v(" "),s("p",[t._v("感谢大家对 "),s("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("scrollview_observer"),s("OutboundLink")],1),t._v(" 的支持,上一篇关于 "),s("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("scrollview_observer"),s("OutboundLink")],1),t._v(" 的最新一篇文章是在2022年的10月份,不知不觉已经过去了8个月,它现在已经变得更加完善和强大,让我们一起来看看都做了哪些更新吧 🥳")]),t._v(" "),s("p",[t._v("版本信息")]),t._v(" "),s("ul",[s("li",[t._v("当前版本: "),s("code",[t._v("1.13.2")])]),t._v(" "),s("li",[t._v("GitHub地址: "),s("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/LinXunFeng/flutter_scrollview_observer"),s("OutboundLink")],1)])]),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("item")]),t._v(" 和正在展示的所有 "),s("code",[t._v("item")]),t._v(",当第一个 "),s("code",[t._v("item")]),t._v(" 或者正在展示的 "),s("code",[t._v("item")]),t._v(" 的对象发生变化时才会调用 "),s("code",[t._v("[onObserve]")]),t._v(" 和 "),s("code",[t._v("[onObserveAll]")]),t._v(" 回调。")]),t._v(" "),s("p",[t._v("但往往在一些场景下,我们想要指定在某个滚动状态下才进行观察,以及观察回调的触发时机,那该怎么办呢?")]),t._v(" "),s("h3",{attrs:{id:"_1、autotriggerobservetypes-参数"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、autotriggerobservetypes-参数"}},[t._v("#")]),t._v(" 1、"),s("code",[t._v("autoTriggerObserveTypes")]),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 keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ObserverAutoTriggerObserveType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" autoTriggerObserveTypes"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\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("enum")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ObserverAutoTriggerObserveType")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n scrollStart"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n scrollUpdate"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n scrollEnd"),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("[.scrollStart, .scrollUpdate, .scrollEnd]")])]),t._v(" "),s("p",[t._v("枚举值说明:")]),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",[s("code",[t._v("scrollStart")])]),t._v(" "),s("td",[t._v("开始滚动")])]),t._v(" "),s("tr",[s("td",[s("code",[t._v("scrollUpdate")])]),t._v(" "),s("td",[t._v("滚动中")])]),t._v(" "),s("tr",[s("td",[s("code",[t._v("scrollEnd")])]),t._v(" "),s("td",[t._v("结束滚动")])])])]),t._v(" "),s("p",[t._v("如在视频列表的场景中,我们可能只需要在列表结束滚动时再去做观察哪个 "),s("code",[t._v("item")]),t._v(" 是第一个,然后进行自动播放,这个时候设置 "),s("code",[t._v("autoTriggerObserveTypes")]),t._v(" 为 "),s("code",[t._v(".scrollEnd")]),t._v(" 即可,同时也避免了大量不必要的计算。")]),t._v(" "),s("h3",{attrs:{id:"_2、triggeronobservetype-参数"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、triggeronobservetype-参数"}},[t._v("#")]),t._v(" 2、"),s("code",[t._v("triggerOnObserveType")]),t._v(" 参数")]),t._v(" "),s("p",[t._v("用于配置触发 "),s("code",[t._v("[onObserve]")]),t._v(" 和 "),s("code",[t._v("[onObserveAll]")]),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("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ObserverTriggerOnObserveType")]),t._v(" triggerOnObserveType"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\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("enum")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ObserverTriggerOnObserveType")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n directly"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n displayingItemsChange"),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(".displayingItemsChange")])]),t._v(" "),s("p",[t._v("枚举值说明:")]),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",[s("code",[t._v("directly")])]),t._v(" "),s("td",[t._v("观察到数据后直接将数据返出")])]),t._v(" "),s("tr",[s("td",[s("code",[t._v("displayingItemsChange")])]),t._v(" "),s("td",[t._v("当列表子部件进出或数量发生变化时将观察到的数据返出")])])])]),t._v(" "),s("p",[t._v("一般是在实时获取 "),s("code",[t._v("item")]),t._v(" 的 "),s("code",[t._v("leadingMarginToViewport")]),t._v("("),s("code",[t._v("item")]),t._v(" 顶部与视窗顶部的距离) 和 "),s("code",[t._v("trailingMarginToViewport")]),t._v("("),s("code",[t._v("item")]),t._v(" 底部与视窗底部的距离)的时候比较常用。")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202306042148379.png",alt:""}})]),t._v(" "),s("h2",{attrs:{id:"三、保持im会话位置功能增强"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#三、保持im会话位置功能增强"}},[t._v("#")]),t._v(" 三、保持IM会话位置功能增强")]),t._v(" "),s("p",[t._v("之前限制只支持插入一条消息时保持会话位置,现在放开支持多条,使用方式不变,效果如下")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202306042147895.gif",alt:""}})]),t._v(" "),s("blockquote",[s("p",[t._v("注:该功能依赖被插入消息前的最新消息视图做为参照去计算偏移量,所以如果一次性插入的消息数太多,导致该参照消息视图无法得到渲染,则该功能会失效,需要你自己去对 "),s("code",[t._v("ScrollView")]),t._v(" 的 "),s("code",[t._v("cacheExtent")]),t._v(" 设置合理的值来尽量避免这个问题!")])]),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("GridView")]),t._v(" 这种常规网格布局,只处理顶部偏移量一致的 "),s("code",[t._v("item")]),t._v(",现在调整了内部处理逻辑,已支持瀑布流这种模式")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202306042146866.png",alt:""}})]),t._v(" "),s("p",[t._v("如上图红线所示,位于第一的 "),s("code",[t._v("item")]),t._v(" 是 "),s("code",[t._v("grid item2")]),t._v(" 和 "),s("code",[t._v("grid item3")]),t._v("。")]),t._v(" "),s("h2",{attrs:{id:"五、支持观察viewport"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#五、支持观察viewport"}},[t._v("#")]),t._v(" 五、支持观察viewport")]),t._v(" "),s("p",[t._v("大家多多少少会遇到这种情况,在 "),s("code",[t._v("CustomScrollView")]),t._v(" 中放置了好多 "),s("code",[t._v("sliver")]),t._v(",需要对它们进行观察,判断哪些 "),s("code",[t._v("sliver")]),t._v(" 正在展示,而不仅仅是观察 "),s("code",[t._v("SliverList")]),t._v(" 和 "),s("code",[t._v("SliverGrid")]),t._v(" 中的 "),s("code",[t._v("item")]),t._v("。")]),t._v(" "),s("p",[t._v("在这里可以使用 "),s("code",[t._v("onObserveViewport")]),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 class-name"}},[t._v("SliverViewObserver")]),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("_buildScrollView")]),s("span",{pre:!0,attrs:{class:"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 sliverContexts"),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 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("grid1Context "),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(" grid1Context"),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 keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("swipeContext "),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(" swipeContext"),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 keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("grid2Context "),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(" grid2Context"),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(";")]),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 onObserveViewport"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),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(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n firstChildCtxInViewport "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" result"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("firstChild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sliverContext"),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("firstChildCtxInViewport "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" grid1Context"),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("debugPrint")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'current first sliver in viewport - gridView1'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("firstChildCtxInViewport "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" swipeContext"),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("debugPrint")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'current first sliver in viewport - swipeView'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("firstChildCtxInViewport "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" grid2Context"),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("debugPrint")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'current first sliver in viewport - gridView2'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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(")")]),t._v("\n")])])]),s("ol",[s("li",[t._v("在 "),s("code",[t._v("sliverContexts")]),t._v(" 回调中返回需要观察的 "),s("code",[t._v("sliver")]),t._v(" 的 "),s("code",[t._v("BuildContext")]),t._v("。")]),t._v(" "),s("li",[t._v("在 "),s("code",[t._v("onObserveViewport")]),t._v(" 回调中拿到观察结果,结果对应的类为 "),s("code",[t._v("SliverViewportObserveModel")])])]),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("SliverViewportObserveModel")]),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("/// 当前 CustomScrollView 所对应的 viewport 实例.")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("RenderViewportBase")]),t._v(" viewport"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 根据 sliverContexts,返回当前观察到的第一个 sliver")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverViewportObserveDisplayingChildModel")]),t._v(" firstChild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 根据 sliverContexts,返回当前观察到的正在展示的所有 sliver")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverViewportObserveDisplayingChildModel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n displayingChildModelList"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("sliver")]),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 class-name"}},[t._v("SliverPadding")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n sliver"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverList")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n delegate"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverChildBuilderDelegate")]),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("context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" index"),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 class-name"}},[t._v("ListTile")]),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 class-name"}},[t._v("Text")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'index - ")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("$")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("index")])]),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 "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[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")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("SliverPadding")]),t._v(",而不是 "),s("code",[t._v("SliverList")]),t._v(",在给 "),s("code",[t._v("sliverContexts")]),t._v(" 传递数据时需要留意,否则观察无效。")]),t._v(" "),s("p",[t._v("那一块的 "),s("code",[t._v("Sliver")]),t._v(" 的 "),s("code",[t._v("BuildContext")]),t._v(" 要怎么获取?")]),t._v(" "),s("p",[t._v("你可以使用 "),s("code",[t._v("GlobalKey")]),t._v(",但这里更建议使用 "),s("code",[t._v("SliverLayoutBuilder")])]),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 class-name"}},[t._v("SliverLayoutBuilder")]),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(" _"),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("// 将 context 记录起来")]),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("sliverCtx "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" sliverCtx "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n \n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverPadding")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n sliver"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverList")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n delegate"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverChildBuilderDelegate")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[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")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("h2",{attrs:{id:"六、支持自定义观察对象和观察逻辑"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#六、支持自定义观察对象和观察逻辑"}},[t._v("#")]),t._v(" 六、支持自定义观察对象和观察逻辑")]),t._v(" "),s("h3",{attrs:{id:"_1、customtargetrenderslivertype-回调"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、customtargetrenderslivertype-回调"}},[t._v("#")]),t._v(" 1、"),s("code",[t._v("customTargetRenderSliverType")]),t._v(" 回调")]),t._v(" "),s("blockquote",[s("p",[t._v("仅支持 "),s("code",[t._v("ListViewObserver")]),t._v(" 和 "),s("code",[t._v("GridViewObserver")])])]),t._v(" "),s("p",[t._v("在保持原来的观察逻辑上,告诉 "),s("code",[t._v("scrollview_observer")]),t._v(" 要处理的 "),s("code",[t._v("RenderSliver")]),t._v(" 类型,目的是为了支持对第三方库构建的滚动视图进行观察。")]),t._v(" "),s("div",{staticClass:"language-dart extra-class"},[s("pre",{pre:!0,attrs:{class:"language-dart"}},[s("code",[t._v("customTargetRenderSliverType"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("renderObj"),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("// 告诉该库它需要观察什么类型的 RenderObject")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里以观察 loading_more_list 的 ListView 为例")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" renderObj "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("is")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ExtendedRenderSliverList")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("loading_more_list")]),t._v(" 中常用的 "),s("code",[t._v("RenderObject")]),t._v(" 供大家对照使用。")]),t._v(" "),s("table",[s("thead",[s("tr",[s("th",[t._v("类型")]),t._v(" "),s("th",[s("code",[t._v("RenderObject")])])])]),t._v(" "),s("tbody",[s("tr",[s("td",[s("code",[t._v("ListView")])]),t._v(" "),s("td",[s("code",[t._v("ExtendedRenderSliverList")])])]),t._v(" "),s("tr",[s("td",[s("code",[t._v("GridView")])]),t._v(" "),s("td",[s("code",[t._v("ExtendedRenderSliverGrid")])])]),t._v(" "),s("tr",[s("td",[s("code",[t._v("WaterfallFlow")])]),t._v(" "),s("td",[s("code",[t._v("RenderSliverWaterfallFlow")])])])])]),t._v(" "),s("h3",{attrs:{id:"_2、customhandleobserve-回调"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、customhandleobserve-回调"}},[t._v("#")]),t._v(" 2、"),s("code",[t._v("customHandleObserve")]),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("customHandleObserve"),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 comment"}},[t._v("// 可以完全自定义你的观察逻辑")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" _obj "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" context"),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(")")]),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("_obj "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("is")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("RenderSliverList")]),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 class-name"}},[t._v("ObserverCore")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("handleListObserve")]),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(" 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("}")]),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("_obj "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("is")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("RenderSliverGrid")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("||")]),t._v(" _obj "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("is")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("RenderSliverWaterfallFlow")]),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("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ObserverCore")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("handleGridObserve")]),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(" 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("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 处理一些由第三方构建的 Sliver")]),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 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 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("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n")])])]),s("p",[t._v("现已将原处理 "),s("code",[t._v("RenderSliverList")]),t._v(" 和 "),s("code",[t._v("RenderSliverGride")]),t._v(" 的逻辑抽离至 "),s("code",[t._v("ObserverCore")]),t._v(" 中供大家自由组合使用。")]),t._v(" "),s("h3",{attrs:{id:"_3、extendedhandleobserve-回调"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_3、extendedhandleobserve-回调"}},[t._v("#")]),t._v(" 3、"),s("code",[t._v("extendedHandleObserve")]),t._v(" 回调")]),t._v(" "),s("blockquote",[s("p",[t._v("仅支持 "),s("code",[t._v("SliverViewObserver")])])]),t._v(" "),s("p",[t._v("该回调用于对原来的观察逻辑进行补充,原来只处理 "),s("code",[t._v("RenderSliverList")]),t._v("、"),s("code",[t._v("RenderSliverFixedExtentList")]),t._v(" 和 "),s("code",[t._v("RenderSliverGrid")]),t._v("。")]),t._v(" "),s("div",{staticClass:"language-dart extra-class"},[s("pre",{pre:!0,attrs:{class:"language-dart"}},[s("code",[t._v("extendedHandleObserve"),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 comment"}},[t._v("// 在对原来的观察逻辑进行拓展")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" _obj "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" context"),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(")")]),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("_obj "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("is")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("RenderSliverWaterfallFlow")]),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 class-name"}},[t._v("ObserverCore")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("handleGridObserve")]),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(" 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("}")]),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("null")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("ObserverCore")]),t._v(" 和以上的几个功能点,实现了可自由观察由第三方构建的滚动视图的功能,不再局限于官方的 "),s("code",[t._v("SliverList")]),t._v(" 和 "),s("code",[t._v("SliverGrid")]),t._v("。")]),t._v(" "),s("p",[t._v("这是一个很有用的功能,它让你的滚动视图的构建变得更加自由,比如观察使用 "),s("a",{attrs:{href:"https://github.com/fluttercandies/waterfall_flow",target:"_blank",rel:"noopener noreferrer"}},[t._v("fluttercandies/waterfall_flow"),s("OutboundLink")],1),t._v(" 构建的瀑布流。")]),t._v(" "),s("h2",{attrs:{id:"七、定位tab的下标计算支持网格类型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#七、定位tab的下标计算支持网格类型"}},[t._v("#")]),t._v(" 七、定位Tab的下标计算支持网格类型")]),t._v(" "),s("p",[t._v("对 "),s("code",[t._v("ObserverUtils.calcAnchorTabIndex")]),t._v(" 进行增强,现支持处理列表和网格的模块下标计算,这里直接看效果图")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202306042148767.gif",alt:""}})]),t._v(" "),s("p",[t._v("GitHub: "),s("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/LinXunFeng/flutter_scrollview_observer"),s("OutboundLink")],1)])])}),[],!1,null,null,null);s.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/131.5606c445.js b/assets/js/131.5606c445.js new file mode 100644 index 000000000..405edb17e --- /dev/null +++ b/assets/js/131.5606c445.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[131],{443:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h2",{attrs:{id:"一、概述"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#一、概述"}},[t._v("#")]),t._v(" 一、概述")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202306111347681.gif",alt:""}})]),t._v(" "),s("p",[t._v("最近忙的一个需求,是要做到如下效果:")]),t._v(" "),s("ol",[s("li",[t._v("瀑布流影音")]),t._v(" "),s("li",[t._v("用户滑动结束后,到达红色线处的为命中的视图,仅命中的视图才可进行影音播放")]),t._v(" "),s("li",[t._v("如果第二次滑动后命中的还是瀑布流上的原来两个 "),s("code",[t._v("item")]),t._v(" ,则交替播放影音")]),t._v(" "),s("li",[t._v("瀑布流的中间会存在一个影音栏目模块,翻页播放视频")]),t._v(" "),s("li",[t._v("瀑布流与影音栏目的播放不可共存")])]),t._v(" "),s("p",[t._v("具体效果如上图所示")]),t._v(" "),s("h2",{attrs:{id:"二、布局"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#二、布局"}},[t._v("#")]),t._v(" 二、布局")]),t._v(" "),s("p",[t._v("上图可以看到了这个需求的整体效果,下面我们来分析一下这个页面的布局实现")]),t._v(" "),s("blockquote",[s("p",[t._v("下面的代码如看到没有使用到的变量,请先忽略,这部分是了解整体的布局情况,所以把一些不相关的代码以 "),s("code",[t._v("...")]),t._v(" 代替了")])]),t._v(" "),s("h3",{attrs:{id:"_1、滚动视图"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、滚动视图"}},[t._v("#")]),t._v(" 1、滚动视图")]),t._v(" "),s("p",[t._v("滚动视图内有多种布局,所以这里使用 "),s("code",[t._v("CustomScrollView")]),t._v(" 来组合各种 "),s("code",[t._v("Sliver")]),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 class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildScrollView")]),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 class-name"}},[t._v("CustomScrollView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n slivers"),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("// Banner")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildBanner")]),s("span",{pre:!0,attrs:{class:"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("_buildSeparator")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("8")]),s("span",{pre:!0,attrs:{class:"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("_buildGridView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("isFirst"),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(" childCount"),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 function"}},[t._v("_buildSeparator")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("8")]),s("span",{pre:!0,attrs:{class:"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("_buildSwipeView")]),s("span",{pre:!0,attrs:{class:"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("_buildSeparator")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("15")]),s("span",{pre:!0,attrs:{class:"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("_buildGridView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("isFirst"),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(",")]),t._v(" childCount"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("table",[s("thead",[s("tr",[s("th",[s("code",[t._v("Sliver")])]),t._v(" "),s("th",[t._v("说明")])])]),t._v(" "),s("tbody",[s("tr",[s("td",[s("code",[t._v("Banner")])]),t._v(" "),s("td",[t._v("一个简单的 "),s("code",[t._v("SliverToBoxAdapter")])])]),t._v(" "),s("tr",[s("td",[t._v("瀑布流1")]),t._v(" "),s("td",[t._v("使用 "),s("code",[t._v("SliverWaterfallFlow")]),t._v(" 构建的瀑布流")])]),t._v(" "),s("tr",[s("td",[t._v("影音栏目")]),t._v(" "),s("td",[t._v("使用 "),s("code",[t._v("PageView")]),t._v(" 构建的翻页视图")])]),t._v(" "),s("tr",[s("td",[t._v("瀑布流2")]),t._v(" "),s("td",[t._v("同 瀑布流1")])])])]),t._v(" "),s("h3",{attrs:{id:"_2、瀑布流"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、瀑布流"}},[t._v("#")]),t._v(" 2、瀑布流")]),t._v(" "),s("p",[t._v("使用第三方库实现瀑布流:"),s("a",{attrs:{href:"https://github.com/fluttercandies/waterfall_flow",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/fluttercandies/waterfall_flow"),s("OutboundLink")],1)]),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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:waterfall_flow/waterfall_flow.dart'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildGridView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n bool isFirst "),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 required int childCount"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverWaterfallFlow")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n gridDelegate"),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 class-name"}},[t._v("SliverWaterfallFlowDelegateWithFixedCrossAxisCount")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n crossAxisCount"),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 mainAxisSpacing"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("15")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n crossAxisSpacing"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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 punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n delegate"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverChildBuilderDelegate")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildContext")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" int index"),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 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 class-name"}},[t._v("WaterfallFlowGridItemView")]),s("span",{pre:!0,attrs:{class:"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(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 childCount"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" childCount"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("看一下 "),s("code",[t._v("WaterfallFlowGridItemView")]),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 class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildBody")]),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 class-name"}},[t._v("Column")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n crossAxisAlignment"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CrossAxisAlignment")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("start"),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 isHit "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildVideo")]),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 function"}},[t._v("_buildCover")]),s("span",{pre:!0,attrs:{class:"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("const")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SizedBox")]),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(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("Text")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'grid item ")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("$")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("selfIndex")])]),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 "),s("span",{pre:!0,attrs:{class:"token class-name"}},[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.0")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("50.0")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("selfIndex "),s("span",{pre:!0,attrs:{class:"token operator"}},[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 "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("如果是命中的"),s("code",[t._v("item")]),t._v(",则展示影音视图,否则展示封面")]),t._v(" "),s("h3",{attrs:{id:"_3、栏目影音"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_3、栏目影音"}},[t._v("#")]),t._v(" 3、栏目影音")]),t._v(" "),s("p",[t._v("为什么用 "),s("code",[t._v("SliverLayoutBuilder")]),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 class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildSwipeView")]),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("isRemoveSwipe"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverToBoxAdapter")]),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SizedBox")]),s("span",{pre:!0,attrs:{class:"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 class-name"}},[t._v("SliverLayoutBuilder")]),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(" _"),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 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 class-name"}},[t._v("SliverToBoxAdapter")]),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 class-name"}},[t._v("WaterfallFlowSwipeView")]),s("span",{pre:!0,attrs:{class:"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(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("接下来看一下 "),s("code",[t._v("WaterfallFlowSwipeView")]),t._v(" 中的 "),s("code",[t._v("build")]),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 function"}},[t._v("@override")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),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 class-name"}},[t._v("BuildContext")]),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 class-name"}},[t._v("Widget")]),t._v(" resultWidget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PageView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("\n controller"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" pageController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n padEnds"),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(",")]),t._v("\n itemBuilder"),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(" index"),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(" isHit "),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(".")]),s("span",{pre:!0,attrs:{class:"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 class-name"}},[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(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("EdgeInsets")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("only")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("right"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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(")")]),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 class-name"}},[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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[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 child"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" isHit "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildVideo")]),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("const")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SizedBox")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("shrink")]),s("span",{pre:!0,attrs:{class:"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("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n itemCount"),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(",")]),t._v("\n onPageChanged"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("index"),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("currentIndex "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),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 currentIndex "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 resultWidget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SizedBox")]),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(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("200")]),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(" resultWidget"),s("span",{pre:!0,attrs:{class:"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(" resultWidget"),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("item")]),t._v(",则展示影音视图,否则什么都不展示。")]),t._v(" "),s("h2",{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",[t._v("判断当前到达红线的是瀑布流还是影音栏目。")]),t._v(" "),s("li",[t._v("当瀑布流的同一水平位置上的多个视图,在列表来回滑动时,轮流播放。")])]),t._v(" "),s("p",[t._v("这里我使用了我开发的一个列表滚动辅助库 "),s("code",[t._v("scrollview_observer")]),t._v(": "),s("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/LinXunFeng/flutter_scrollview_observer"),s("OutboundLink")],1),t._v(",")]),t._v(" "),s("p",[t._v("虽然 "),s("code",[t._v("pub.dev")]),t._v(" 上有可以监听列表滚动位置的库,但是侵入性很强,并且功能还不够强大,应付不来上面的需求,特别是第2个功能点,简直是魔鬼需求,然而,用我开发的库 "),s("code",[t._v("scrollview_observer")]),t._v(" 就可以轻松应付这2个技术难点。")]),t._v(" "),s("ul",[s("li",[t._v("针对难点1: 只需要使用 "),s("code",[t._v("SliverViewObserver")]),t._v(" 包裹 "),s("code",[t._v("CustomScrollView")]),t._v(" 就可以对其进行观察,这样会适时返回可视区域中所有的 "),s("code",[t._v("Sliver")]),t._v(" 和 "),s("code",[t._v("item")]),t._v(" 信息,再配合 "),s("code",[t._v("leadingOffset")]),t._v(" 参数设置观察的偏移量,即可轻松实现达到红线的需求,然后在 "),s("code",[t._v("onObserveViewport")]),t._v(" 回调中可以得知达到红线的是瀑布流还是影音栏目。")]),t._v(" "),s("li",[t._v("针对难点2: 在 "),s("code",[t._v("SliverViewObserver")]),t._v(" 的 "),s("code",[t._v("onObserveAll")]),t._v(" 回调参数中可以拿到此时瀑布流中被命中的所有 "),s("code",[t._v("item")]),t._v(" 的信息,再通过简单的计算,即可完成轮流播放的功能。")])]),t._v(" "),s("p",[t._v("接下来我们进入实战部分。")]),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("CustomScrollView")]),t._v(" 来实现滚动视图部分,其中分了上下两个瀑布流 "),s("code",[t._v("Sliver")]),t._v(",中间一个栏目影音 "),s("code",[t._v("Sliver")]),t._v(",在滚动视图结束滚动后,只需要做如下主要逻辑:")]),t._v(" "),s("ol",[s("li",[t._v("哪个 "),s("code",[t._v("Sliver")]),t._v(" 到达了红线")]),t._v(" "),s("li",[t._v("如果是瀑布流,则获取命中的 "),s("code",[t._v("item")]),t._v(" 下标,然后进行影音播放")]),t._v(" "),s("li",[t._v("如果是影音栏目,则按当前正在展示的 "),s("code",[t._v("item")]),t._v(" 进行影音播放")])]),t._v(" "),s("p",[t._v("先定义命中的 "),s("code",[t._v("Sliver")]),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("enum")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WaterFlowHitType")]),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 firstGrid"),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 swipe"),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 secondGrid"),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("// 命中的下标")]),t._v("\nint hitIndex "),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"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 命中的类型")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WaterFlowHitType")]),t._v(" hitType "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WaterFlowHitType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("firstGrid"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("h3",{attrs:{id:"_1、sliverviewobserver-的使用"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、sliverviewobserver-的使用"}},[t._v("#")]),t._v(" 1、"),s("code",[t._v("SliverViewObserver")]),t._v(" 的使用")]),t._v(" "),s("p",[t._v("使用 "),s("code",[t._v("SliverViewObserver")]),t._v(" 包裹 "),s("code",[t._v("CustomScrollView")]),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("\ndouble observeOffset "),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\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverViewObserver")]),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("_buildBody")]),s("span",{pre:!0,attrs:{class:"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 leadingOffset"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" observeOffset"),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 autoTriggerObserveTypes"),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("[")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ObserverAutoTriggerObserveType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("scrollEnd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 comment"}},[t._v("// 设置触发返回观察结果回调的时机,这里直接返回结果")]),t._v("\n triggerOnObserveType"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ObserverTriggerOnObserveType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("directly"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n extendedHandleObserve"),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 comment"}},[t._v("// 拓展原来的观察处理逻辑,瀑布流是使用第三方库构建的,")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 所以在这里需要告知 scrollview_observer 如何去观察它。")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" _obj "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ObserverUtils")]),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("(")]),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 keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("_obj "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("is")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("RenderSliverWaterfallFlow")]),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 class-name"}},[t._v("ObserverCore")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("handleGridObserve")]),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(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n fetchLeadingOffset"),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 operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" observeOffset"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 keyword"}},[t._v("return")]),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("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sliverContexts"),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 comment"}},[t._v("// 返回目标 Sliver 对应的 BuildContext")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),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("grid1Context "),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(" grid1Context"),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 keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("swipeContext "),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(" swipeContext"),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 keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("grid2Context "),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(" grid2Context"),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(";")]),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 onObserveViewport"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),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(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 观察 viewport,在这里可以知道第一个 Sliver 是哪个")]),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 onObserveAll"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("resultMap"),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("// 观察瀑布流的 item")]),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("blockquote",[s("p",[s("code",[t._v("onObserveViewport")]),t._v(" 和 "),s("code",[t._v("onObserveAll")]),t._v(" 同时存在时,"),s("code",[t._v("onObserveViewport")]),t._v(" 回调先走!")])]),t._v(" "),s("p",[t._v("在 "),s("code",[t._v("sliverContexts")]),t._v(" 参数回调中,返回需要被观察的 "),s("code",[t._v("Sliver")]),t._v(",在这个场景下,瀑布流和影音栏目都需要被观察,所以这里返回了他们相应的 "),s("code",[t._v("BuildContext")]),t._v(",相应的,我们就需要先把它们记录起来。")]),t._v(" "),s("p",[t._v("记录瀑布流的 "),s("code",[t._v("BuildContext")])]),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 class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildGridView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n bool isFirst "),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 required int childCount"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverWaterfallFlow")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 delegate"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverChildBuilderDelegate")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildContext")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" int index"),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("WaterFlowHitType")]),t._v(" selfType"),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("isFirst"),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("// 记录BuildContext")]),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("grid1Context "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" grid1Context "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" context"),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 selfType "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WaterFlowHitType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("firstGrid"),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("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("grid2Context "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" grid2Context "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n selfType "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WaterFlowHitType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("secondGrid"),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("// 瀑布流 item")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WaterfallFlowGridItemView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n selfIndex"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n selfType"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" selfType"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n hitIndex"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" hitIndex"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n hitType"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" hitType"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("BuildContext")])]),t._v(" "),s("blockquote",[s("p",[t._v("实现 "),s("code",[t._v("SliverLayoutBuilder")]),t._v(" 得到"),s("code",[t._v("BuildContext")])])]),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 class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildSwipeView")]),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 class-name"}},[t._v("SliverLayoutBuilder")]),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(" _"),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("// 记录BuildContext")]),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("swipeContext "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" swipeContext "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" context"),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 class-name"}},[t._v("SliverToBoxAdapter")]),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 class-name"}},[t._v("WaterfallFlowSwipeView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("hitType"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" hitType"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("h3",{attrs:{id:"_2、找出命中的-sliver"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、找出命中的-sliver"}},[t._v("#")]),t._v(" 2、找出命中的 "),s("code",[t._v("Sliver")])]),t._v(" "),s("p",[t._v("现在开始来处理观察 "),s("code",[t._v("Viewport")]),t._v(" 的逻辑,通过观察 "),s("code",[t._v("Viewport")]),t._v(" 就可以得知哪个是第一个 "),s("code",[t._v("Sliver")])]),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("// 记录到达红线的 Sliver")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildContext")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" firstChildCtxInViewport"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\nonObserveViewport"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),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(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 记录第一个 Sliver")]),t._v("\n firstChildCtxInViewport "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" result"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("firstChild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sliverContext"),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("firstChildCtxInViewport "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" grid1Context"),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")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WaterFlowHitType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("firstGrid "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" hitType"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),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 hitType "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WaterFlowHitType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("firstGrid"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 重置命中的下标,在 onObserveAll 回调中给 hitIndex 赋值时会使用到")]),t._v("\n hitIndex "),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 comment"}},[t._v("// 这里不调用 setState,只记录数据,在 onObserveAll 更新命中下标后再刷新页面")]),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 keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("firstChildCtxInViewport "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" swipeContext"),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")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WaterFlowHitType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("swipe "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" hitType"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),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("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 hitType "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WaterFlowHitType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("swipe"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("firstChildCtxInViewport "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" grid2Context"),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 comment"}},[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("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WaterFlowHitType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("secondGrid "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" hitType"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n hitType "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WaterFlowHitType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("secondGrid"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n hitIndex "),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 punctuation"}},[t._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("onObserveViewport")]),t._v(" 中的逻辑,我们已经确定了当前命中的 "),s("code",[t._v("Sliver")]),t._v(" 是哪个,并通过 "),s("code",[t._v("firstChildCtxInViewport")]),t._v(" 属性记录了起来。")]),t._v(" "),s("p",[t._v("当命中的是影音栏目时,则更新 "),s("code",[t._v("hitType")]),t._v(" 为 "),s("code",[t._v("WaterFlowHitType.swipe")]),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 function"}},[t._v("@override")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),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 class-name"}},[t._v("BuildContext")]),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 class-name"}},[t._v("Widget")]),t._v(" resultWidget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PageView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("\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 itemBuilder"),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(" index"),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("// 判断当前的 item 是否命中")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" isHit "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WaterFlowHitType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("swipe "),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("hitType "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" currentIndex "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" index"),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 class-name"}},[t._v("Padding")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 child"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),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 "),s("span",{pre:!0,attrs:{class:"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 child"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" isHit "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildVideo")]),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("const")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SizedBox")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("shrink")]),s("span",{pre:!0,attrs:{class:"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("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 onPageChanged"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("index"),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")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("currentIndex "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),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 currentIndex "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("span",{pre:!0,attrs:{class:"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(" resultWidget"),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("hitType")]),t._v(" 非瀑布流类型,所以瀑布流会将影音视图变成封面。")]),t._v(" "),s("blockquote",[s("p",[t._v("注:本例为功能示例,所以以上都是通过 "),s("code",[t._v("setState")]),t._v(" 简单地进行视图变化处理,在实际应用场景中可根据自身的情况,实现局部刷新逻辑")])]),t._v(" "),s("p",[t._v("影音栏目的播放逻辑就完成了,接下来就是处理瀑布流的影音。")]),t._v(" "),s("h3",{attrs:{id:"_3、处理瀑布流的命中-item-下标"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_3、处理瀑布流的命中-item-下标"}},[t._v("#")]),t._v(" 3、处理瀑布流的命中 "),s("code",[t._v("item")]),t._v(" 下标")]),t._v(" "),s("p",[t._v("通过 "),s("code",[t._v("onObserveAll")]),t._v(" 观察 "),s("code",[t._v("Sliver")]),t._v(",通过它我们可以知道瀑布流 "),s("code",[t._v("Slive")]),t._v(" 中哪些是命中的 "),s("code",[t._v("item")]),t._v("。")]),t._v(" "),s("div",{staticClass:"language-dart extra-class"},[s("pre",{pre:!0,attrs:{class:"language-dart"}},[s("code",[t._v("onObserveAll"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("resultMap"),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("// 根据记录的第一个 Sliver 的 BuildContext,取出相应的观察结果")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" result "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" resultMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("firstChildCtxInViewport"),s("span",{pre:!0,attrs:{class:"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("firstChildCtxInViewport "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" grid1Context"),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 comment"}},[t._v("// 如果当前的第一个 Sliver 不是相应的类型,则直接返回")]),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("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WaterFlowHitType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("firstGrid "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" hitType"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),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")]),t._v(" "),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(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("null")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("||")]),t._v(" result "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("is!")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("GridViewObserveModel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 取出命中的 item 的下标(瀑布流中一次命中的可能有多个)")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" firstIndexList "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" result"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("firstGroupChildList"),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(" e"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 comment"}},[t._v("// 处理瀑布流命中的逻辑(由于第二个瀑布流中的处理逻辑相同,所以抽了方法)")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("handleGridHitIndex")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("firstIndexList"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("firstChildCtxInViewport "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" grid2Context"),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")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WaterFlowHitType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("secondGrid "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" hitType"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),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("result "),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 operator"}},[t._v("||")]),t._v(" result "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("is!")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("GridViewObserveModel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),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(" firstIndexList "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" result"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("firstGroupChildList"),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(" e"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 function"}},[t._v("handleGridHitIndex")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("firstIndexList"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("p",[t._v("在 "),s("code",[t._v("handleGridHitIndex")]),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 function"}},[t._v("handleGridHitIndex")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("int"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" firstIndexList"),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("firstIndexList"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isEmpty"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),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 int targetIndex "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" firstIndexList"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("indexOf")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("hitIndex"),s("span",{pre:!0,attrs:{class:"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("targetIndex "),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 "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 没找着,置为0")]),t._v("\n targetIndex "),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 "),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 comment"}},[t._v("// 找着了,则取下一个,为了实现交替播放")]),t._v("\n targetIndex "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" targetIndex "),s("span",{pre:!0,attrs:{class:"token operator"}},[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 "),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("targetIndex "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">=")]),t._v(" firstIndexList"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("length"),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("// 但是如果超过了结果数组的最大下标,则置为0")]),t._v("\n targetIndex "),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 "),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("// 更新 hitIndex 并刷新页面")]),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 hitIndex "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" firstIndexList"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("targetIndex"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("h2",{attrs:{id:"五、最后"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#五、最后"}},[t._v("#")]),t._v(" 五、最后")]),t._v(" "),s("p",[t._v("通过上述示例的讲解,相信你对 "),s("code",[t._v("scrollview_observer")]),t._v(" 的使用又更加清楚,如果你也觉得这个库好用,请不吝给个 "),s("code",[t._v("Star")]),t._v(" 👍")]),t._v(" "),s("p",[t._v("GitHub: "),s("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/LinXunFeng/flutter_scrollview_observer"),s("OutboundLink")],1)])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/132.cfe3f25f.js b/assets/js/132.cfe3f25f.js new file mode 100644 index 000000000..9891c38fd --- /dev/null +++ b/assets/js/132.cfe3f25f.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[132],{444:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h2",{attrs:{id:"一、概述"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#一、概述"}},[t._v("#")]),t._v(" 一、概述")]),t._v(" "),s("p",[t._v("在 "),s("RouterLink",{attrs:{to:"/pages/cd4fd1/"}},[t._v("【Flutter - 快速实现聊天会话列表的效果,完美💯】")]),t._v(" 一文中介绍了常规聊天场景下如何保持消息位置及其实现原理,本篇将在此基础上完成 "),s("code",[t._v("ChatGPT")]),t._v(" 这种生成式消息保持位置的功能。")],1),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202306181254155.gif",alt:""}})]),t._v(" "),s("p",[t._v("如上图所示,操作与现象:")]),t._v(" "),s("ol",[s("li",[t._v("点击右上角的按钮后,会插入一条消息,模拟向 "),s("code",[t._v("ChatGPT")]),t._v(" 发出问题")]),t._v(" "),s("li",[t._v("然后间隔 "),s("code",[t._v("1s")]),t._v(" 会自动插入一条生成式消息,模拟 "),s("code",[t._v("ChatGPT")]),t._v(" 在不断回复我们的问题")])]),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("ListView")]),t._v(",不过需要结合 "),s("code",[t._v("ChatObserver")]),t._v(" 做一些配置")]),t._v(" "),s("h3",{attrs:{id:"_1、chatobserver-配置"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、chatobserver-配置"}},[t._v("#")]),t._v(" 1、"),s("code",[t._v("ChatObserver")]),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("// 实例化 ListObserverController")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 由于滚动视图使用的是 ListView,所以选择 ListObserverController")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 否则请选择相应的 ObserverController")]),t._v("\nobserverController "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ListObserverController")]),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(" scrollController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 关闭模块定位的偏移缓存功能,因为IM经常添加或删除消息,偏移的缓存容易过时")]),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("cacheJumpIndexOffset "),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\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 实例化 ChatScrollObserver")]),t._v("\nchatObserver "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatScrollObserver")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("observerController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 滚动视图的偏移量超过 5 时才会启用保持IM消息位置的功能")]),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("fixedPositionOffset "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 内部在切换 isShrinkWrap 的值时会触发该回调,")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 触发时局部刷新列表视图即可,这里因为是 Demo 的缘故,做法比较简单粗暴")]),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("toRebuildScrollViewCallback "),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(" "),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("{")]),s("span",{pre:!0,attrs:{class:"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("h3",{attrs:{id:"_2、listview-配置"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、listview-配置"}},[t._v("#")]),t._v(" 2、"),s("code",[t._v("ListView")]),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 class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildListView")]),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 class-name"}},[t._v("Widget")]),t._v(" resultWidget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ListView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 内含处理保持IM消息位置的核心逻辑")]),t._v("\n physics"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatObserverClampingScrollPhysics")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("observer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" chatObserver"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("EdgeInsets")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("only")]),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(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" right"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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(" top"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("15")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" bottom"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("15")]),s("span",{pre:!0,attrs:{class:"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("// 切换滚动视图的高度模式,当消息不满一屏时,该值为 true,使消息在顶部展示,")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 消息超一屏时为 false,消息在底部展示,chatObserver 内部会适时变更该值")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 如果你希望一直都是底部展示,可以注释该行")]),t._v("\n shrinkWrap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" chatObserver"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isShrinkWrap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 消息从底部往上开始排序,所以来新消息时应该插入到 0 的位置")]),t._v("\n reverse"),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("\n controller"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" scrollController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n itemBuilder"),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("context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" index"),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 class-name"}},[t._v("ChatItemWidget")]),s("span",{pre:!0,attrs:{class:"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(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 itemCount"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" chatModels"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("length"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 \n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 对滚动视图监听")]),t._v("\n resultWidget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ListViewObserver")]),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(" observerController"),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(" resultWidget"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 \n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 重点,如果你希望不满一屏时在顶部展示消息,而又不生效时,")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 记得如下所示设置 alignment 为 Alignment.topCenter")]),t._v("\n resultWidget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[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(" resultWidget"),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 class-name"}},[t._v("Alignment")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("topCenter"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("return")]),t._v(" resultWidget"),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("h2",{attrs:{id:"三、实战"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#三、实战"}},[t._v("#")]),t._v(" 三、实战")]),t._v(" "),s("p",[s("code",[t._v("AppBar")]),t._v(" 右边的按钮,点击时模拟发出一条问题消息,然后等待1s后 "),s("code",[t._v("ChatGPT")]),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 class-name"}},[t._v("IconButton")]),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 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 "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("stopMsgUpdateStream")]),s("span",{pre:!0,attrs:{class:"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("_addMessage")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("isOwn"),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(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 等待1s")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[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("const")]),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(")")]),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("insertGenerativeMsg")]),s("span",{pre:!0,attrs:{class:"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 icon"),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 class-name"}},[t._v("Icon")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Icons")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("add_comment"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("停止旧生成式消息的更新,控制同一时间只允许存在一条生成式消息")]),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("stopMsgUpdateStream")]),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 timer"),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("cancel")]),s("span",{pre:!0,attrs:{class:"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 timer "),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("插入一条新消息方法")]),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("_addMessage")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n required bool isOwn"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 在符合条件的情况下保持当前的IM消息位置")]),t._v("\n chatObserver"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("standby")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("changeCount"),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 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 chatModels"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("insert")]),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(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatDataHelper")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("createChatModel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("isOwn"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" isOwn"),s("span",{pre:!0,attrs:{class:"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")])])]),s("p",[t._v("插入生成式消息,模拟 "),s("code",[t._v("ChatGPT")]),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("insertGenerativeMsg")]),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 function"}},[t._v("stopMsgUpdateStream")]),s("span",{pre:!0,attrs:{class:"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("_addMessage")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("isOwn"),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("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 开始模拟接收更新的消息数据")]),t._v("\n int count "),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 timer "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Timer")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("periodic")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),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("milliseconds"),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(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("timer"),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")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("count "),s("span",{pre:!0,attrs:{class:"token operator"}},[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(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("stopMsgUpdateStream")]),s("span",{pre:!0,attrs:{class:"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")]),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 count"),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 comment"}},[t._v("// 取出最新的生成式消息")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" model "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" chatModels"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("first"),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("final")]),t._v(" newString "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("${")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("model"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("content")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("-1+1'")])]),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("final")]),t._v(" newModel "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatModel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("isOwn"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" model"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isOwn"),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(" newString"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n chatModels"),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("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" newModel"),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 chatObserver"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("standby")]),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 mode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatScrollObserverHandleMode")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("generative"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// changeCount: 1,")]),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 comment"}},[t._v("// 后面会解释该模式的使用")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// chatObserver.standby(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// changeCount: 1,")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// mode: ChatScrollObserverHandleMode.specified,")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// refItemRelativeIndex: 2,")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// refItemRelativeIndexAfterUpdate: 2,")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// );")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 刷新滚动视图,可局部刷新,Demo 缘故,做法比较简单粗暴")]),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("{")]),s("span",{pre:!0,attrs:{class:"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")])])]),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("standby")]),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("standby")]),s("span",{pre:!0,attrs:{class:"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("// 滚动视图的 BuildContext")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 当布局复杂使 scrollview_observer 无法顺利自动找到滚动视图的 BuildContext 时才需要传入")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 如 CustomScrollView,或 ObserverWidget 包裹的 child 中有多个 ListView、GridView")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildContext")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" sliverContext"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 是否是删除消息的操作,它不会启用保持位置功能,而是可能切换 isShrinkWrap 的值")]),t._v("\n bool isRemove "),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("// 变更的IM消息数")]),t._v("\n int changeCount "),s("span",{pre:!0,attrs:{class:"token operator"}},[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 "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 处理模式")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatScrollObserverHandleMode")]),t._v(" mode "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatScrollObserverHandleMode")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("normal"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 刷新滚动视图前,参照的相对item的下标")]),t._v("\n int refItemRelativeIndex "),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 "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 刷新滚动视图后,参照的相对item的下标")]),t._v("\n int refItemRelativeIndexAfterUpdate "),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"),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 keyword"}},[t._v("enum")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatScrollObserverHandleMode")]),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 comment"}},[t._v("/// 插入或删除IM消息的时候使用")]),t._v("\n normal"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 生成式模式")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 专门处理 ChatGPT 这种不断更新消息的情况")]),t._v("\n generative"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 指定模式")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 在该模式下我们可以指定参照的IM消息的相对下标")]),t._v("\n specified"),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("h3",{attrs:{id:"_1、常规模式"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、常规模式"}},[t._v("#")]),t._v(" 1、常规模式")]),t._v(" "),s("p",[t._v("常规模式为默认的处理模式,应用于日常的 "),s("code",[t._v("IM")]),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("_addMessage")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("int count"),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("// 进入待命状态,保持IM消息位置")]),t._v("\n chatObserver"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("standby")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("changeCount"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" count"),s("span",{pre:!0,attrs:{class:"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("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 needIncrementUnreadMsgCount "),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("for")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" i "),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(" i "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" count"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),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 chatModels"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("insert")]),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(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatDataHelper")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("createChatModel")]),s("span",{pre:!0,attrs:{class:"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("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("删除消息")]),t._v(" "),s("div",{staticClass:"language-dart extra-class"},[s("pre",{pre:!0,attrs:{class:"language-dart"}},[s("code",[t._v("chatObserver"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("standby")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("isRemove"),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(")")]),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 chatModels"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("removeAt")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("h3",{attrs:{id:"_2、生成式模式-仿chatgpt"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、生成式模式-仿chatgpt"}},[t._v("#")]),t._v(" 2、生成式模式(仿"),s("code",[t._v("ChatGPT")]),t._v(")")]),t._v(" "),s("p",[t._v("专门处理 "),s("code",[t._v("ChatGPT")]),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("final")]),t._v(" model "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" chatModels"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("first"),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(" newString "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("${")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("model"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("content")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("-1+1'")])]),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(" newModel "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatModel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("isOwn"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" model"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isOwn"),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(" newString"),s("span",{pre:!0,attrs:{class:"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("\nchatModels"),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("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" newModel"),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("\nchatObserver"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("standby")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n mode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatScrollObserverHandleMode")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("generative"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// changeCount: 1,")]),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 comment"}},[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("{")]),s("span",{pre:!0,attrs:{class:"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("ul",[s("li",[t._v("指定处理模式 "),s("code",[t._v("mode")]),t._v(" 为 "),s("code",[t._v(".generative")])]),t._v(" "),s("li",[s("code",[t._v("changeCount")]),t._v(" 默认值为 "),s("code",[t._v("1")]),t._v(",传与不传一样")])]),t._v(" "),s("p",[t._v("内部会根据 "),s("code",[t._v("changeCount")]),t._v(" 来计算并记录参照消息的相对下标(关于相对下标的内容会在 "),s("code",[t._v("指定模式")]),t._v(" 一节中说明),这意味着该模式支持连续的多条生成式消息保持位置,比如最新的两条消息都是生成式的情况是支持。")]),t._v(" "),s("p",[t._v("而如果最新的三条消息,"),s("code",[t._v("0")]),t._v(" 和 "),s("code",[t._v("2")]),t._v(" 都是生成式消息而 "),s("code",[t._v("1")]),t._v(" 不是,又或者同一时间插入消息和更新生成式消息,此时该模式是无法很好的支持保持消息位置这一功能的,那有什么办法呢?你可以使用 "),s("code",[t._v("指定模式")])]),t._v(" "),s("h3",{attrs:{id:"_3、指定模式"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_3、指定模式"}},[t._v("#")]),t._v(" 3、指定模式")]),t._v(" "),s("p",[t._v("顾名思义,可以指定参照的 "),s("code",[t._v("IM")]),t._v(" 消息的相对下标,从而可以自由的使用保持消息位置的功能,当然了,自由也意味着传入的数据和需要了解的会更多!")]),t._v(" "),s("p",[t._v("我们先来了解一下什么是参照的 "),s("code",[t._v("IM")]),t._v(" 消息的相对下标?")]),t._v(" "),s("blockquote",[s("p",[t._v("注: 滚动视图内已渲染的 "),s("code",[t._v("item")]),t._v(" 不一定会被显示出来,如果下文中提及的滚动视图渲染的 "),s("code",[t._v("item")]),t._v(" 让你不好理解,你可以直接认为是屏幕中正在展示的 "),s("code",[t._v("item")]),t._v("。")])]),t._v(" "),s("p",[t._v("假如当前是正在看最新消息的情况,滚动视图内渲染了 "),s("code",[t._v("item0")]),t._v(" 到 "),s("code",[t._v("item4")]),t._v(",其相对下标为 "),s("code",[t._v("0")]),t._v(" 到 "),s("code",[t._v("4")]),t._v("。")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v(" trailing relativeIndex\n----------------- -----------------\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" item4 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" item3 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" item2 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" item1 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" item0 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" \n----------------- -----------------\n leading\n")])])]),s("p",[t._v("如果此时你在翻看历史消息,滚动视图内渲染了 "),s("code",[t._v("item10")]),t._v(" 到 "),s("code",[t._v("item14")]),t._v(",其相对下标也为 "),s("code",[t._v("0")]),t._v(" 到 "),s("code",[t._v("4")]),t._v("。")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v(" trailing relativeIndex\n----------------- -----------------\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" item14 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" item13 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" item12 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" item11 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" item10 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" \n----------------- -----------------\n leading\n")])])]),s("p",[t._v("这里 "),s("code",[t._v("0")]),t._v(" 到 "),s("code",[t._v("4")]),t._v(" 的下标即为相对下标,我们来使用该模式("),s("code",[t._v(".specified")]),t._v(")来完成 "),s("code",[t._v(".generative")]),t._v(" 模式的功能")]),t._v(" "),s("p",[t._v("在上述示例中,离发出问题间隔了1秒后,"),s("code",[t._v("ChatGPT")]),t._v(" 会开始回答问题,此时我们插入生成式的消息,并不断更新该消息内容。")]),t._v(" "),s("blockquote",[s("p",[t._v("注意: 插入消息的方法内已经做了保持消息位置的处理,所以我们的关注点在于更新消息")])]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v(" trailing relativeIndex\n----------------- -----------------\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" item4 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" item3 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" item2 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" item1 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" item0 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" \n----------------- -----------------\n leading\n")])])]),s("p",[t._v("假设此时 "),s("code",[t._v("item0")]),t._v(" 为生成式消息,它的消息内容在不断变多,如果放任不管,"),s("code",[t._v("item0")]),t._v(" 以上的消息会逐渐被往上顶,因此在这里我们的目的是不管 "),s("code",[t._v("item0")]),t._v(" 如何变化,都需要保持 "),s("code",[t._v("item1")]),t._v(" 及以上消息的位置,所以 "),s("code",[t._v("item1")]),t._v(" 就成为了我们的参照消息,它此刻的下标为 "),s("code",[t._v("1")]),t._v(",而生成式消息的变化前后,"),s("code",[t._v("item1")]),t._v(" 的下标一直都是 "),s("code",[t._v("1")]),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("chatObserver"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("standby")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n changeCount"),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 "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 设置处理模式为 .specified")]),t._v("\n mode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatScrollObserverHandleMode")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("specified"),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 refItemRelativeIndex"),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 "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 滚动视图更新后,参照消息的相对下标")]),t._v("\n refItemRelativeIndexAfterUpdate"),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"),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("refItemRelativeIndex")]),t._v(" 和 "),s("code",[t._v("refItemRelativeIndexAfterUpdate")]),t._v(" 应该指向同一条消息!")]),t._v(" "),s("p",[t._v("其实参照消息在理论上只要是 "),s("code",[t._v("item0")]),t._v(" 以上的已经渲染的消息即可,即上述的参照消息的相对下标也可以是 "),s("code",[t._v("2")]),t._v("、 "),s("code",[t._v("3")]),t._v(" 和 "),s("code",[t._v("4")]),t._v("。")]),t._v(" "),s("blockquote",[s("p",[t._v("注: 如果更新滚动视图后参照的消息无法得到渲染,则该功能就会失效,所以建议还是选择当前发生变化的消息的上一条消息的相对下标~")])]),t._v(" "),s("p",[t._v("比较有意思的是,假设我们在发出问题后,往上翻页了,1秒后滚动视图渲染的消息是 "),s("code",[t._v("item10")]),t._v(" 到 "),s("code",[t._v("item14")])]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v(" trailing relativeIndex\n----------------- -----------------\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" item14 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" item13 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" item12 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" item11 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" item10 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" \n----------------- -----------------\n leading\n")])])]),s("p",[t._v("此时你的参照消息的相对下标就可以为 "),s("code",[t._v("0")]),t._v(" 了,但是你需要判断当前已渲染的第一个 "),s("code",[t._v("item")]),t._v(" 是否为生成式消息,这就很麻烦,没有必要。")]),t._v(" "),s("blockquote",[s("p",[t._v("总结来说,参照的消息不可以为发生变化的消息本身,而是为滚动视图在更新前后都会被渲染的 "),s("code",[t._v("item")]),t._v(" 即可!")])]),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("scrollview_observer")]),t._v(" 的使用又更加清楚,如果你也觉得这个库好用,请不吝给个 "),s("code",[t._v("Star")]),t._v(" 👍")]),t._v(" "),s("p",[t._v("GitHub: "),s("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/LinXunFeng/flutter_scrollview_observer"),s("OutboundLink")],1)])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/133.9616d190.js b/assets/js/133.9616d190.js new file mode 100644 index 000000000..46d2fc6e5 --- /dev/null +++ b/assets/js/133.9616d190.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[133],{445:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h2",{attrs:{id:"一、概述"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#一、概述"}},[t._v("#")]),t._v(" 一、概述")]),t._v(" "),s("p",[t._v("最近遇到的一个需求上的优化点,在一个详情页中存在提交反馈信息的表单模块,这里简单的模拟了一下,这是运行后的效果")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202308131919003.gif",alt:"优化前"}})]),t._v(" "),s("p",[t._v("设计师验收的时候觉得这里有一点需要做下优化,那就是表单视图并不大,提交按钮需要完整的显示出来,讲道理要手动去计算偏移量还是挺麻烦的,但是如果结合我的 "),s("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("LinXunFeng/flutter_scrollview_observer"),s("OutboundLink")],1),t._v(" 这个库就完全难不倒我们,咔咔两下就搞定了,优化后的效果如下:")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202308131920819.gif",alt:"优化后"}})]),t._v(" "),s("p",[s("code",[t._v("Demo")]),t._v(" 链接:"),s("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_scrollview_observer/blob/main/example/lib/features/scene/scrollview_form_demo/scrollview_form_demo_page.dart",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/LinXunFeng/flutter_scrollview_observer/blob/main/example/lib/features/scene/scrollview_form_demo/scrollview_form_demo_page.dart"),s("OutboundLink")],1)]),t._v(" "),s("p",[t._v("接下来我讲一下具体的实现步骤。")]),t._v(" "),s("h2",{attrs:{id:"二、布局"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#二、布局"}},[t._v("#")]),t._v(" 二、布局")]),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("使用 "),s("code",[t._v("ListView")]),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 class-name"}},[t._v("ScrollController")]),t._v(" scrollController "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ScrollController")]),s("span",{pre:!0,attrs:{class:"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 class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildScrollView")]),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 class-name"}},[t._v("Widget")]),t._v(" resultWidget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ListView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("\n controller"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" scrollController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n itemBuilder"),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(" index"),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("formIndex "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" index"),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("_buildForm")]),s("span",{pre:!0,attrs:{class:"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 keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildImage")]),s("span",{pre:!0,attrs:{class:"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 itemCount"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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 punctuation"}},[t._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 keyword"}},[t._v("return")]),t._v(" resultWidget"),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("h3",{attrs:{id:"表单模块"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#表单模块"}},[t._v("#")]),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 class-name"}},[t._v("FocusNode")]),t._v(" formFocusNode "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FocusNode")]),s("span",{pre:!0,attrs:{class:"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 class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildForm")]),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 class-name"}},[t._v("Widget")]),t._v(" resultWidget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Form")]),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 class-name"}},[t._v("Column")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n crossAxisAlignment"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CrossAxisAlignment")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("start"),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 generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[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 "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),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("\n "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Feedback'")])]),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 class-name"}},[t._v("TextStyle")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n fontSize"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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 fontWeight"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FontWeight")]),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("\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 comment"}},[t._v("// 输入框")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextField")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n focusNode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" formFocusNode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 comment"}},[t._v("// 提交按钮")]),t._v("\n "),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 width"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" double"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("infinity"),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[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(",")]),t._v("\n alignment"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[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 margin"),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 class-name"}},[t._v("EdgeInsets")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("only")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("top"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("10.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 class-name"}},[t._v("TextButton")]),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 class-name"}},[t._v("Text")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Submit'")])]),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 comment"}},[t._v("// 使输入框失去焦点")]),t._v("\n formFocusNode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("unfocus")]),s("span",{pre:!0,attrs:{class:"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(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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(".")]),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(" resultWidget"),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("h2",{attrs:{id:"三、实战"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#三、实战"}},[t._v("#")]),t._v(" 三、实战")]),t._v(" "),s("p",[t._v("由于滚动视图使用的是 "),s("code",[t._v("ListView")]),t._v(",所以我们使用对应 "),s("code",[t._v("ListViewObserver")]),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("// 将滚动视图的 ScrollController 传给 ListObserverController")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ListObserverController")]),t._v(" observerController "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ListObserverController")]),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(" scrollController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\nresultWidget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ListViewObserver")]),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(" observerController"),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 autoTriggerObserveTypes"),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 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("_buildScrollView")]),s("span",{pre:!0,attrs:{class:"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 comment"}},[t._v("// onObserve: (resultModel) {}")]),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("blockquote",[s("p",[t._v("注:请留意一下注释掉的 "),s("code",[t._v("onObserve")]),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("final")]),t._v(" int formIndex "),s("span",{pre:!0,attrs:{class:"token operator"}},[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(";")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 监听输入框的焦点状态")]),t._v("\nformFocusNode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("addListener")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("handleFormFocus"),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\n"),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("handleFormFocus")]),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 "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("formFocusNode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("hasFocus"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),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 class-name"}},[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("const")]),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("milliseconds"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("600")]),s("span",{pre:!0,attrs:{class:"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("final")]),t._v(" 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(" observerController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("dispatchOnceObserve")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n isForce"),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(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 强制观察,不用对比上次的观察结果")]),t._v("\n isDependObserveCallback"),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(",")]),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("span",{pre:!0,attrs:{class:"token comment"}},[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("(")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("result"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isSuccess"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 根据下标从观察结果中找出表单对应的数据")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" formResultModel "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n result"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("observeResult"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("displayingChildModelList"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("firstWhere")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("element"),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(" element"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("index "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" formIndex"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("formResultModel "),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 keyword"}},[t._v("return")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n \n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 进行滚动,使表单视图底部紧贴键盘显示出来")]),t._v("\n observerController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("controller"),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("animateTo")]),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 formResultModel"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("scrollOffset "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v(" formResultModel"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("trailingMarginToViewport"),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 keyword"}},[t._v("const")]),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("milliseconds"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("200")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Curves")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ease"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("一些额外说明:")]),t._v(" "),s("ul",[s("li",[t._v("等待键盘完全展示出来的时长("),s("code",[t._v("600 ms")]),t._v(")是测试出来的同时适用于iOS和安卓的一个值,如果你有更好的方式,可以留言分享一下 😁")]),t._v(" "),s("li",[t._v("在 "),s("code",[t._v("1.16.0")]),t._v(" 版本之前,如果对应的观察回调(如:"),s("code",[t._v("onObserve")]),t._v(")没有实现的话,内部的观察逻辑就无法进行下去,提前结束。而在 "),s("code",[t._v("1.16.0")]),t._v(" 版本之后对 "),s("code",[t._v("dispatchOnceObserve")]),t._v(" 方法进行了增强,可以通过对 "),s("code",[t._v("isDependObserveCallback")]),t._v(" 设置为 "),s("code",[t._v("false")]),t._v(" 避开这个逻辑,并且返回值也改造成了 "),s("code",[t._v("Future")]),t._v(",可直接拿到观察结果,相当方便!")]),t._v(" "),s("li",[s("code",[t._v("scrollOffset")]),t._v(" 是当前滚动视图的偏移量")]),t._v(" "),s("li",[s("code",[t._v("trailingMarginToViewport")]),t._v(" 是指当前 "),s("code",[t._v("item")]),t._v(" 到 "),s("code",[t._v("viewport")]),t._v(" 的底部距离,即 "),s("code",[t._v("表单模块视图的底部")]),t._v(" 到 "),s("code",[t._v("滚动视图的视窗底部")]),t._v(" 的间距。")]),t._v(" "),s("li",[t._v("由于上述功能我们是依赖于键盘弹出来的视图高度发生变化的特性,键盘出来时视窗大小受到挤压缩小,所以 "),s("code",[t._v("Scaffold")]),t._v(" 这里的 "),s("code",[t._v("resizeToAvoidBottomInset")]),t._v(" 属性请不要设置为 "),s("code",[t._v("false")]),t._v("。如果设置了 "),s("code",[t._v("false")]),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("return")]),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 resizeToAvoidBottomInset"),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("\n body"),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(".")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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",[t._v("observerController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("controller"),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("animateTo")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n formResultModel"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("scrollOffset "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v(" formResultModel"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("trailingMarginToViewport"),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 keyword"}},[t._v("const")]),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("milliseconds"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("200")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Curves")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ease"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202308131921953.png",alt:"图解"}})]),t._v(" "),s("p",[t._v("咱们配图食用,当键盘完全展示出来后,滚动视图的的视窗变小了, 由红色区域变成了蓝色区域,而此时表单模块也完全展示出来了,其底部到视窗之间的间距就是 "),s("code",[t._v("trailingMarginToViewport")]),t._v(",为正数,但是我们需要让它紧贴底部,所以此时滚动视图的偏移量是多了的,需要减去这个 "),s("code",[t._v("trailingMarginToViewport")]),t._v(",这便是最终偏移量计算的逻辑。")])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/134.972bd9fb.js b/assets/js/134.972bd9fb.js new file mode 100644 index 000000000..a28612002 --- /dev/null +++ b/assets/js/134.972bd9fb.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[134],{446:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[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("1/2")]),t._v(" 曝光统计的计算方式,顾名思义就是模块露出的大小超过自身大小的 "),s("code",[t._v("50%")]),t._v(" 时,需要触发一次统计,并记录起来防止被反复统计,当少于 "),s("code",[t._v("50%")]),t._v(" 时会将曝光记录进行重置。")]),t._v(" "),s("p",[t._v("一般会用于统计在列表页中投放的广告和详情页中某些广告模块的曝光。")]),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("item")]),t._v(" 的自身显示占比还是比较麻烦的,所以普遍我们会优先去找和使用已存在的解决方案。")]),t._v(" "),s("p",[t._v("那想必大家脑海中第一个想到的便是谷歌自家的 "),s("a",{attrs:{href:"https://pub.dev/packages/visibility_detector/versions",target:"_blank",rel:"noopener noreferrer"}},[t._v("visibility_detector"),s("OutboundLink")],1),t._v(",这个库也确实很好用,常规场景下呢我也比较推荐大家使用它的,因为它真的太方便了!不过我所遇到的一种场景它却无法胜任,那就是在 "),s("code",[t._v("CustomScrollView")]),t._v(" 中存在 "),s("code",[t._v("SliverPersistentHeader")]),t._v(" 的情况,它的计算结果会不准确,如下所示。")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202308261638786.gif",alt:""}})]),t._v(" "),s("p",[t._v("注意看蓝色的 "),s("code",[t._v("Middle Sliver")]),t._v(" 视图,当它刚被 "),s("code",[t._v("AppBar")]),t._v(" 挡住时自身显示占比还是 "),s("code",[t._v("1")]),t._v(",直到超出了屏幕的上方才开始发生变化,当被 "),s("code",[t._v("AppBar")]),t._v(" 完全遮挡时值为 "),s("code",[t._v("0.58")]),t._v("~")]),t._v(" "),s("p",[t._v("不过这里我着重介绍一下另一个方案,那就是使用我这个库("),s("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("flutter_scrollview_observer"),s("OutboundLink")],1),t._v(") 去快速获取 "),s("code",[t._v("item")]),t._v(" 自身显示占比,而且不会有上述的 "),s("code",[t._v("bug")]),t._v("。")]),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("ListView")]),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("final")]),t._v(" observerController "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ListObserverController")]),s("span",{pre:!0,attrs:{class:"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 class-name"}},[t._v("ListViewObserver")]),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("_buildListView")]),s("span",{pre:!0,attrs:{class:"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 triggerOnObserveType"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ObserverTriggerOnObserveType")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("directly"),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(" observerController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n onObserve"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("resultModel"),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("// 从观察结果中拿到正在展示的所有 item 的数据")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" models "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" resultModel"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("displayingChildModelList"),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("final")]),t._v(" indexList "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" models"),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 operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" e"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("index"),s("span",{pre:!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 comment"}},[t._v("// 取出所有 item 的自身显示占比")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" displayPercentageList "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n models"),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 operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" e"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("displayPercentage"),s("span",{pre:!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 function"}},[t._v("debugPrint")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'index -- ")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("$")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("indexList")])]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v(" -- ")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("$")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("displayPercentageList")])]),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 "),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("item")]),t._v(" 的自身显示占比就是这么简单,通过使用对应的 "),s("code",[t._v("WidgetObserver")]),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("observerController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("dispatchOnceObserve")]),s("span",{pre:!0,attrs:{class:"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("h2",{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 keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:scrollview_observer/scrollview_observer.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("mixin")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VisibilityExposureMixin")]),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("// 记录 item 已曝光的 Map")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Map")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("dynamic")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" bool"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" exposureRecordMap "),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("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 重置所有 item 的曝光记录")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("resetExposureRecordMap")]),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 exposureRecordMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("clear")]),s("span",{pre:!0,attrs:{class:"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 comment"}},[t._v("/// 处理滚动视图中 item 的曝光")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// ")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// [resultModel] 监听结果(基类是 ObserveModel, 传 onObserve 回调中的值,或 onObserveAll 中根据 BuildContext 取出来的值)")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// [toExposeDisplayPercent] 当自身显示占比超过该值时视为曝光且记录起来,否则重置曝光记录")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// [recordKeyCallback] 返回用于记录 item 已曝光的 key,不实现则使用下标")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// [needExposeCallback] 用于确定对应下标的 item 是否参与曝光计算逻辑,不实现则为 true")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// [toExposeCallback] 满足曝光条件后的回调")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("handleExposure")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n required "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("dynamic")]),t._v(" resultModel"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n double toExposeDisplayPercent "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0.5")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("dynamic")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Function")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("int index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" recordKeyCallback"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n bool "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Function")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("int index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" needExposeCallback"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n required "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Function")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("int index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" toExposeCallback"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ObserveDisplayingChildModelMixin")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" displayingChildModelList "),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("]")]),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("resultModel "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("is")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ListViewObserveModel")]),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 displayingChildModelList "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" resultModel"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("displayingChildModelList"),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 keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("resultModel "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("is")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("GridViewObserveModel")]),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 displayingChildModelList "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" resultModel"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("displayingChildModelList"),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("for")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" displayingChildModel "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" displayingChildModelList"),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(" index "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" displayingChildModel"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("index"),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(" recordKey "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" recordKeyCallback"),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("call")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("index"),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(" index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 让外部告诉我们 index 对应的 item 是否需要参与曝光计算逻辑")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" needExpose "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" needExposeCallback"),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("call")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("index"),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("true")]),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("(")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("needExpose"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("continue")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// debugPrint('item : $index - ${displayingChildModel.displayPercentage}');")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 判断 item 自身显示占比是否超过 [toExposeDisplayPercent]")]),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("displayingChildModel"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("displayPercentage "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" toExposeDisplayPercent"),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 exposureRecordMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("recordKey"),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 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(" "),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 comment"}},[t._v("// 满足暴露条件")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" haveExposure "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" exposureRecordMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("recordKey"),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 keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("haveExposure"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("continue")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("toExposeCallback")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n exposureRecordMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("recordKey"),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 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("}")]),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("逻辑:")]),t._v(" "),s("ol",[s("li",[t._v("达到 "),s("code",[t._v("1/2")]),t._v(" 后触发曝光统计,并记录起来,防触发多次请求")]),t._v(" "),s("li",[t._v("小于 "),s("code",[t._v("1/2")]),t._v(" 时重置当前 "),s("code",[t._v("item")]),t._v(" 的曝光记录")])]),t._v(" "),s("p",[t._v("使用:")]),t._v(" "),s("p",[t._v("混入 "),s("code",[t._v("VisibilityExposureMixin")])]),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(" _VisibilityListViewPageState "),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 generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VisibilityListViewPage")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VisibilityExposureMixin")]),t._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("onObserve")]),t._v(" 中调用 "),s("code",[t._v("handleExposure")]),t._v(" 方法")]),t._v(" "),s("div",{staticClass:"language-dart extra-class"},[s("pre",{pre:!0,attrs:{class:"language-dart"}},[s("code",[t._v("onObserve"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("resultModel"),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("handleExposure")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n resultModel"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" resultModel"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n needExposeCallback"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("index"),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("// 只有下标为 6 的 item 需要计算是否曝光")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" index "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("6")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 toExposeCallback"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("index"),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 function"}},[t._v("debugPrint")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Exposure -- ")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("$")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("index")])]),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 "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("最终看下效果吧,注意看红色视图和控制台的输出")]),t._v(" "),s("p",[t._v("曝光左下角 "),s("code",[t._v("ListView")]),t._v(" 中下标为 "),s("code",[t._v("6")]),t._v(" 的红色 "),s("code",[t._v("item")])]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202308261639314.gif",alt:""}})]),t._v(" "),s("p",[t._v("曝光左下角 "),s("code",[t._v("SliverGrid")]),t._v(" 中下标为 "),s("code",[t._v("6")]),t._v(" 的紫色 "),s("code",[t._v("item")])]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202308261639591.gif",alt:""}})]),t._v(" "),s("p",[t._v("Demo链接:"),s("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_scrollview_observer/tree/main/example/lib/features/scene/visibility_demo",target:"_blank",rel:"noopener noreferrer"}},[t._v("visibility_demo"),s("OutboundLink")],1)])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/135.a29157ee.js b/assets/js/135.a29157ee.js new file mode 100644 index 000000000..426d6ceeb --- /dev/null +++ b/assets/js/135.a29157ee.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[135],{447:function(t,e,r){"use strict";r.r(e);var _=r(8),v=Object(_.a)({},(function(){var t=this,e=t._self._c;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("2023年10月13日的那一天,发生了我职业生涯中的两件大事:")]),t._v(" "),e("h3",{attrs:{id:"_1、加入-flutter-组织"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_1、加入-flutter-组织"}},[t._v("#")]),t._v(" 1、加入 "),e("code",[t._v("Flutter")]),t._v(" 组织")]),t._v(" "),e("p",[t._v("经过 "),e("code",[t._v("@AlexV525")]),t._v(" 大佬的细心查验,我最终获得了他的推荐,并收到 "),e("code",[t._v("@Hixie")]),t._v(" 发来的加入 "),e("code",[t._v("Flutter")]),t._v(" 组织的邀请邮件,正式成为了该组织成员。")]),t._v(" "),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202310152349040.png",alt:"推荐"}})]),t._v(" "),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202310152354920.png",alt:"邀请"}})]),t._v(" "),e("h3",{attrs:{id:"_2、加入fluttercandies-组织"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_2、加入fluttercandies-组织"}},[t._v("#")]),t._v(" 2、加入"),e("code",[t._v("FlutterCandies")]),t._v(" 组织")]),t._v(" "),e("p",[t._v("我带着 "),e("a",{attrs:{href:"https://github.com/fluttercandies/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("flutter_scrollview_observer"),e("OutboundLink")],1),t._v(" 开源库加入到 "),e("code",[t._v("FlutterCandies")]),t._v(" 组织。")]),t._v(" "),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202310152354680.png",alt:"邀请"}})]),t._v(" "),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202310152354307.png",alt:"flutter_scrollview_observer"}})]),t._v(" "),e("p",[t._v("正所谓独乐乐不如众乐乐,我将这两个好消息告诉了移动端的同事们,并请他们每个人喝一杯下午茶 😄")]),t._v(" "),e("p",[t._v("现在回想起来,一切都好似梦幻般的在进行中,我竟然在这一天内加入到 "),e("code",[t._v("Flutter")]),t._v(" 和 "),e("code",[t._v("FlutterCandies")]),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("code",[t._v("Flutter")]),t._v(" 的开发经历~")]),t._v(" "),e("h3",{attrs:{id:"_1、入门小白"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_1、入门小白"}},[t._v("#")]),t._v(" 1、入门小白")]),t._v(" "),e("p",[t._v("其实我也并不是完全小白,早到2018年年末就接触学习了 "),e("code",[t._v("Flutter")]),t._v(" 的相关知识,不过学完并没有机会去运用,平日里还是在痛苦的搞 "),e("code",[t._v("iOS")]),t._v(" 原生开发,加上项目是 "),e("code",[t._v("OC")]),t._v(" 与 "),e("code",[t._v("Swift")]),t._v(" 混编,导致日常代码无提示,编译慢,且造成电脑极其卡顿,无法同时做其它事情,效率低下,基于此种种原因,事业部最终决定转 "),e("code",[t._v("Flutter")]),t._v(" 来提升开发效率。")]),t._v(" "),e("p",[t._v("所以从2022年开始,我们就分配了使用 "),e("code",[t._v("Flutter")]),t._v(" 对现有业务进行重构的技术推动任务,就这样,我开始正式接触 "),e("code",[t._v("Flutter")]),t._v(" 框架,那时还需要花费大量时间才能完成一个页面~ 😂")]),t._v(" "),e("h3",{attrs:{id:"_2、维护开源库"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_2、维护开源库"}},[t._v("#")]),t._v(" 2、维护开源库")]),t._v(" "),e("p",[t._v("慢慢的,我们开始将 "),e("code",[t._v("Flutter")]),t._v(" 应用于项目新需求页面的研发,而在第一个 "),e("code",[t._v("Flutter")]),t._v(" 需求里要求视频列表页滚动停止后,位于指定偏移的影音视图能够自动播放,一上来就是高难度,经过摸索 "),e("code",[t._v("RenderObject")]),t._v(" 的相关知识,最终成功攻克。")]),t._v(" "),e("p",[t._v("类似效果:\n"),e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202205291628769.gif",alt:""}})]),t._v(" "),e("p",[t._v("当然攻克需求难点不是最终目的,我一想到后面要用到类似功能时又要再写一遍 "),e("code",[t._v("RenderObject")]),t._v(" 相关的东西就头疼,于是做了一个决定,封装成 "),e("code",[t._v("Flutter")]),t._v(" 组件并开源!是的,这便是 "),e("a",{attrs:{href:"https://github.com/fluttercandies/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("flutter_scrollview_observer"),e("OutboundLink")],1),t._v(" 这个开源库的诞生背景。")]),t._v(" "),e("p",[t._v("后面需求提及的功能越来越复杂,我也不断去加强 "),e("a",{attrs:{href:"https://github.com/fluttercandies/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("flutter_scrollview_observer"),e("OutboundLink")],1),t._v(" 的功能,帮助我快速完成功能研发,我也在 "),e("code",[t._v("Flutter")]),t._v(" 开源的道路上越走越远~")]),t._v(" "),e("p",[t._v("截止到今天关于开源库 "),e("a",{attrs:{href:"https://github.com/fluttercandies/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("flutter_scrollview_observer"),e("OutboundLink")],1),t._v(" 有这三大功能点:")]),t._v(" "),e("ol",[e("li",[e("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_scrollview_observer/wiki/1%E3%80%81%E7%9B%91%E5%90%AC%E6%BB%9A%E5%8A%A8%E8%A7%86%E5%9B%BE%E4%B8%AD%E6%AD%A3%E5%9C%A8%E6%98%BE%E7%A4%BA%E7%9A%84%E5%AD%90%E9%83%A8%E4%BB%B6",target:"_blank",rel:"noopener noreferrer"}},[t._v("监听滚动视图中正在显示的子部件"),e("OutboundLink")],1)]),t._v(" "),e("li",[e("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_scrollview_observer/wiki/2%E3%80%81%E6%BB%9A%E5%8A%A8%E5%88%B0%E6%8C%87%E5%AE%9A%E4%B8%8B%E6%A0%87%E4%BD%8D%E7%BD%AE",target:"_blank",rel:"noopener noreferrer"}},[t._v("滚动到指定下标位置"),e("OutboundLink")],1)]),t._v(" "),e("li",[e("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_scrollview_observer/wiki/3%E3%80%81%E8%81%8A%E5%A4%A9%E4%BC%9A%E8%AF%9D",target:"_blank",rel:"noopener noreferrer"}},[t._v("聊天会话"),e("OutboundLink")],1)])]),t._v(" "),e("p",[t._v("相关技术文章有:")]),t._v(" "),e("ol",[e("li",[e("RouterLink",{attrs:{to:"/pages/0422cb/"}},[t._v("Flutter - 获取ListView当前正在显示的Widget信息")])],1),t._v(" "),e("li",[e("RouterLink",{attrs:{to:"/pages/beb840/"}},[t._v("Flutter - 列表滚动定位超强辅助库,墙裂推荐!🔥")])],1),t._v(" "),e("li",[e("RouterLink",{attrs:{to:"/pages/cd4fd1/"}},[t._v("Flutter - 快速实现聊天会话列表的效果,完美💯")])],1),t._v(" "),e("li",[e("RouterLink",{attrs:{to:"/pages/5f9bf6/"}},[t._v("Flutter - 船新升级😱支持观察第三方构建的滚动视图💪")])],1),t._v(" "),e("li",[e("RouterLink",{attrs:{to:"/pages/15d73d/"}},[t._v("Flutter - 瀑布流交替播放视频 🎞")])],1),t._v(" "),e("li",[e("RouterLink",{attrs:{to:"/pages/26b199/"}},[t._v("Flutter - IM保持消息位置大升级(支持ChatGPT生成式消息) 🤖")])],1),t._v(" "),e("li",[e("RouterLink",{attrs:{to:"/pages/326c98/"}},[t._v("Flutter - 滚动视图中的表单防遮挡 🗒")])],1),t._v(" "),e("li",[e("RouterLink",{attrs:{to:"/pages/d18561/"}},[t._v("Flutter - 秒杀1/2曝光统计 📊")])],1)]),t._v(" "),e("h3",{attrs:{id:"_3、提交-pr"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_3、提交-pr"}},[t._v("#")]),t._v(" 3、提交 "),e("code",[t._v("PR")])]),t._v(" "),e("p",[t._v("说起来也是机缘巧合,我当时学习 "),e("code",[t._v("iOS")]),t._v(" 逆向知识的时候因追求个性化,修改了 "),e("code",[t._v("LLDB")]),t._v(" 的提示符,导致我在相当长的一段时间内无法直接运行 "),e("code",[t._v("Flutter")]),t._v(" 项目,然后发现可以先跑原生项目,再 "),e("code",[t._v("Attach")]),t._v(" 去调试,靠着这种曲线救国的方式度日 😭")]),t._v(" "),e("p",[t._v("后面我实在受不了了,再加上过年期间需求不那么紧急,就决定对 "),e("code",[t._v("flutter_tools")]),t._v(" 进行一番探索,最终找到问题所在,并对原逻辑进行调整,支持自定义 "),e("code",[t._v("LLDB")]),t._v(" 提示符的场景,接着迈出了 "),e("code",[t._v("Flutter PR")]),t._v(" 的第一步!")]),t._v(" "),e("p",[t._v("截止到今天关于 "),e("code",[t._v("Flutter PR")]),t._v(" 的相关总结技术文章有:")]),t._v(" "),e("ol",[e("li",[e("RouterLink",{attrs:{to:"/pages/5c4b50/"}},[t._v("Flutter - 我给官方提PR,解决run命令卡住问题 😃")])],1),t._v(" "),e("li",[e("RouterLink",{attrs:{to:"/pages/4517cb/"}},[t._v("Flutter - 探索run命令到底做了什么 🤔")])],1),t._v(" "),e("li",[e("RouterLink",{attrs:{to:"/pages/6caa6f/"}},[t._v("Flutter - 引擎调试(iOS篇)🛠")])],1),t._v(" "),e("li",[e("RouterLink",{attrs:{to:"/pages/614da2/"}},[t._v("Flutter - 引擎调试bug到提交PR实战 🐞")])],1)]),t._v(" "),e("p",[t._v("接下来在整个 "),e("code",[t._v("2023")]),t._v(" 年里,我的日常开源活动就是维护 "),e("a",{attrs:{href:"https://github.com/fluttercandies/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("flutter_scrollview_observer"),e("OutboundLink")],1),t._v(" 这个开源组件,写文章,以及提 "),e("code",[t._v("Flutter PR")]),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("好了,回忆与感慨到此结束,加入组织并不是终点,而是一个美好的开始。")]),t._v(" "),e("p",[t._v("最后再提一下 "),e("code",[t._v("scrollview_observer")]),t._v(" 的新地址: "),e("a",{attrs:{href:"https://github.com/fluttercandies/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/fluttercandies/flutter_scrollview_observer"),e("OutboundLink")],1)]),t._v(" "),e("p",[t._v("希望大家能够多多支持,点个 "),e("code",[t._v("Star")]),t._v(",我是 "),e("code",[t._v("LinXunFeng")]),t._v(",本篇到此结束,我们下次再见!")])])}),[],!1,null,null,null);e.default=v.exports}}]); \ No newline at end of file diff --git a/assets/js/136.43d150dd.js b/assets/js/136.43d150dd.js new file mode 100644 index 000000000..3d1c96283 --- /dev/null +++ b/assets/js/136.43d150dd.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[136],{448:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h2",{attrs:{id:"一、概述"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#一、概述"}},[t._v("#")]),t._v(" 一、概述")]),t._v(" "),s("p",[t._v("相信大家都很熟悉微信的通讯录列表功能,而实现该效果可能会直接选用现有的已封装好的开源库,比如:"),s("a",{attrs:{href:"https://github.com/flutterchina/azlistview",target:"_blank",rel:"noopener noreferrer"}},[t._v("azlistview"),s("OutboundLink")],1),t._v(",这些库都很优秀,能快速帮助我们完成功能研发,但是会有一些小问题,比如:")]),t._v(" "),s("ul",[s("li",[t._v("依赖了一些其它第三方开源库(如:"),s("code",[t._v("scrollable_positioned_list")]),t._v("),但限定的版本可能与自己项目中的冲突")]),t._v(" "),s("li",[t._v("可能不支持一些刷新组件(如:"),s("code",[t._v("easy_refresh")]),t._v(")")]),t._v(" "),s("li",[t._v("功能和布局的自定义受限(如:无法自定义 "),s("code",[t._v("Header")]),t._v(" 和 "),s("code",[t._v("Footer")]),t._v(",列表 "),s("code",[t._v("item")]),t._v(" 无复选功能)")])]),t._v(" "),s("p",[t._v("更要命的是,如果哪天产品实然要求能在 "),s("code",[t._v("ListView")]),t._v(" 模式和 "),s("code",[t._v("GridView")]),t._v(" 模式之间切换显示,就真的想寄了。 😭")]),t._v(" "),s("p",[t._v("所以说保持使用 "),s("code",[t._v("Flutter")]),t._v(" 官方提供的滚动视图才是最可靠的,灵活度高,那本篇文章就跟着我的步伐,一起来实战快速手搓一个微信通讯录列表吧。")]),t._v(" "),s("p",[t._v("先来看看效果:")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202310291925983.gif",alt:""}})]),t._v(" "),s("h2",{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",[t._v("数据定位,防跳动")]),t._v(" "),s("li",[t._v("字母列表交互")])]),t._v(" "),s("h3",{attrs:{id:"_1、数据定位"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、数据定位"}},[t._v("#")]),t._v(" 1、数据定位")]),t._v(" "),s("p",[t._v("滑动右侧字母列表,跳转到对应字母的数据模块位置时,需要考虑数据长度问题,如果数据量够,那数据内容自然是从顶部开始展示,但如果数据量不够,则以列表的最大偏移去显示,说白了就是显示列表的底部数据,且保持页面不动,这里就需要考虑如何避免跳动问题了。")]),t._v(" "),s("p",[t._v("不过我们今天的主角 "),s("a",{attrs:{href:"https://github.com/fluttercandies/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("flutter_scrollview_observer"),s("OutboundLink")],1),t._v(" 已经帮我们处理了这个跳动问题,所以这一点就不再是难点,我们在代码中尽管去调 "),s("code",[t._v("jumpTo")]),t._v(" 就可以了。")]),t._v(" "),s("h3",{attrs:{id:"_2、字母列表"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、字母列表"}},[t._v("#")]),t._v(" 2、字母列表")]),t._v(" "),s("p",[t._v("在手指触摸字母列表视图时,需要实时返回所对应的下标,以及字母视图所对应的偏移,这样才能使游标视图悬浮到对应位置。")]),t._v(" "),s("p",[t._v("手指触摸可以使用 "),s("code",[t._v("GestureDetector")]),t._v(" 这个官方组件,该组件提供了一些触摸回调,比如 "),s("code",[t._v("onVerticalDragUpdate")]),t._v(",我们可以通过回调里的 "),s("code",[t._v("details")]),t._v(" 参数拿到此时手指在列表中的偏移量,直接通过 "),s("code",[t._v("details.localPosition.dy")]),t._v(" 就可以取到了。")]),t._v(" "),s("p",[t._v("重点来了,怎么根据上述取到的手指偏移量,知道此时对应的下标呢?🧐")]),t._v(" "),s("p",[t._v("转换一下思路,我们可以通过 "),s("a",{attrs:{href:"https://github.com/fluttercandies/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("flutter_scrollview_observer"),s("OutboundLink")],1),t._v(" 来获取当前显示的第一个 "),s("code",[t._v("item")]),t._v(",如果不干预计算,那第一个 "),s("code",[t._v("item")]),t._v(" 肯定永远是 "),s("code",[t._v("A")]),t._v(",所以我们还需要实时指定一个开始计算的偏移量,就可以正常获取到我们理想的第一个正在显示 "),s("code",[t._v("item")]),t._v(" 的数据了。")]),t._v(" "),s("p",[t._v("接下来让我们一起来开始实战吧~")]),t._v(" "),s("h2",{attrs:{id:"三、实战"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#三、实战"}},[t._v("#")]),t._v(" 三、实战")]),t._v(" "),s("h3",{attrs:{id:"_1、联系人数据"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、联系人数据"}},[t._v("#")]),t._v(" 1、联系人数据")]),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 comment"}},[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("AzListContactModel")]),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("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" section"),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("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" names"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AzListContactModel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n required "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("this")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("section"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n required "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("this")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("names"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 comment"}},[t._v("/// 存放联系人模型的数组")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AzListContactModel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" contactList "),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("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 生成联系人数据")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("generateContactData")]),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("// 以 ASCII码 的方式去遍历生成 A-Z 的数据")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" a "),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 class-name"}},[t._v("Utf8Codec")]),s("span",{pre:!0,attrs:{class:"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("encode")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"A"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("first"),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(" z "),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 class-name"}},[t._v("Utf8Codec")]),s("span",{pre:!0,attrs:{class:"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("encode")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Z"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("first"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n int pointer "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" a"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("while")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("pointer "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">=")]),t._v(" a "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" pointer "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<=")]),t._v(" z"),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(" character "),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 class-name"}},[t._v("Utf8Codec")]),s("span",{pre:!0,attrs:{class:"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("decode")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Uint8List")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("fromList")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("pointer"),s("span",{pre:!0,attrs:{class:"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 contactList"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AzListContactModel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n section"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" character"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 为测试数据量不够的情况,所以这里设置了最大生成 8 个元素")]),t._v("\n names"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("generate")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Random")]),s("span",{pre:!0,attrs:{class:"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("nextInt")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("8")]),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("index"),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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("$")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("character")])]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token interpolation"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("$")]),s("span",{pre:!0,attrs:{class:"token expression"}},[t._v("index")])]),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 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 pointer"),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("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("h3",{attrs:{id:"_2、页面布局"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、页面布局"}},[t._v("#")]),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 comment"}},[t._v("// 滚动视图对应的 controller")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ScrollController")]),t._v(" scrollController "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ScrollController")]),s("span",{pre:!0,attrs:{class:"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("// SliverViewObserver 对应的 controller")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里需要传入 scrollController,这样 observerController 的 jumpTo 方法才能生效,内部的跳转功能就是用 scrollController 去实现的")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverObserverController")]),t._v(" observerController "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverObserverController")]),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(" scrollController"),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("// 存放字母下标和所对应的 sliver 的 BuildContext")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Map")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("int"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildContext")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" sliverContextMap "),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("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),s("p",[t._v("联系人滚动视图使用 "),s("code",[t._v("CustomScrollView")]),t._v(" 去实现,"),s("code",[t._v("slivers")]),t._v(" 里存放了所有字母对应的联系人列表 "),s("code",[t._v("SliverList")])]),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 class-name"}},[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 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 class-name"}},[t._v("SliverViewObserver")]),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(" observerController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sliverContexts"),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 comment"}},[t._v("// 返回 字母模块 所对应的所有 sliver 的 BuildContext")]),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(" sliverContextMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("values"),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 comment"}},[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 class-name"}},[t._v("CustomScrollView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 controller"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" scrollController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n slivers"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" contactList"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("mapIndexed")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),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("_buildSliver")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" model"),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("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 comment"}},[t._v("// 悬浮游标")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildCursor")]),s("span",{pre:!0,attrs:{class:"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 class-name"}},[t._v("Positioned")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n top"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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 bottom"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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 right"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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 child"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildIndexBar")]),s("span",{pre:!0,attrs:{class:"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(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n")])])]),s("p",[t._v("构建每一个字母下的联系人列表 "),s("code",[t._v("SliverList")])]),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 class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildSliver")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n required int index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n required "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AzListContactModel")]),t._v(" model"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 没有数据,则返回 SliverToBoxAdapter")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" names "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" model"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("names"),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("names"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isEmpty"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverToBoxAdapter")]),s("span",{pre:!0,attrs:{class:"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("// 创建 SliverList(当然你可以创建 SliverGrid)")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),t._v(" resultWidget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverList")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n delegate"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverChildBuilderDelegate")]),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("context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" itemIndex"),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("// 存放 SliverList 对应的 BuildContext")]),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("sliverContextMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("index"),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(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n sliverContextMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" context"),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("// 返回 item 视图,这个没什么特别的")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AzListItemView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("name"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" names"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("itemIndex"),s("span",{pre:!0,attrs:{class:"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 childCount"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" names"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("length"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 comment"}},[t._v("// 通过 flutter_sticky_header 这个库来实现字母悬浮视图")]),t._v("\n resultWidget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverStickyHeader")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n header"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),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 height"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("44.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(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Color")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("fromARGB")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("255")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("243")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("244")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("246")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[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("16.0")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),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 class-name"}},[t._v("Alignment")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("centerLeft"),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 class-name"}},[t._v("Text")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n model"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("section"),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 keyword"}},[t._v("const")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[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(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 sliver"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" resultWidget"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("return")]),t._v(" resultWidget"),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("h3",{attrs:{id:"_3、字母列表"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_3、字母列表"}},[t._v("#")]),t._v(" 3、字母列表")]),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 comment"}},[t._v("/// 字母列表视图的父视图的key")]),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("final")]),t._v(" indexBarContainerKey "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("GlobalKey")]),s("span",{pre:!0,attrs:{class:"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("/// 从联系人数据里取出所有的字母")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("get")]),t._v(" symbols "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" contactList"),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 operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" e"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("section"),s("span",{pre:!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\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildIndexBar")]),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 class-name"}},[t._v("Container")]),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(" indexBarContainerKey"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 child"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AzListIndexBar")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 父视图的key,有印象即可,下面会用到")]),t._v("\n parentKey"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" indexBarContainerKey"),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 symbols"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" symbols"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n onSelectionUpdate"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" cursorOffset"),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(".")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 onSelectionEnd"),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 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(",")]),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("上面的两个处理回调是用来更新游标视图的,所以这里先不讲它。")]),t._v(" "),s("p",[t._v("接着来讲讲 "),s("code",[t._v("AzListIndexBar")]),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 class-name"}},[t._v("ListObserverController")]),t._v(" observerController "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ListObserverController")]),s("span",{pre:!0,attrs:{class:"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("/// 记录当前手指所在的偏移量")]),t._v("\ndouble observeOffset "),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\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 字母列表视图的整体布局")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@override")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),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 class-name"}},[t._v("BuildContext")]),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 comment"}},[t._v("// ListViewObserver 用来观察 ListView 滚动视图")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),t._v(" resultWidget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ListViewObserver")]),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 child"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildListView")]),s("span",{pre:!0,attrs:{class:"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(" observerController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 动态返回当前手指所在的偏移量,用于指定开始计算的偏移,使观察可以得到理想的第一个 item")]),t._v("\n dynamicLeadingOffset"),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 operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" observeOffset"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 comment"}},[t._v("// 触摸监听")]),t._v("\n resultWidget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("GestureDetector")]),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 onVerticalDragDown"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" _onGestureHandler"),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 onVerticalDragUpdate"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" _onGestureHandler"),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 onVerticalDragCancel"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" _onGestureEnd"),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 onVerticalDragEnd"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" _onGestureEnd"),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(" resultWidget"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("return")]),t._v(" resultWidget"),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 comment"}},[t._v("// 结束/取消 触摸")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_onGestureEnd")]),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 comment"}},[t._v("// 直接告诉外部,结束滑动了")]),t._v("\n widget"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("onSelectionEnd"),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("call")]),s("span",{pre:!0,attrs:{class:"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("手指按下和滑动的时候,需要显示并更新游标上的标题,所以在字母列表视图侧,可以提供一些数据给到外部")]),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 comment"}},[t._v("// 该方法是核心")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_onGestureHandler")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("dynamic")]),t._v(" details"),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("// details 的类型有可能是 DragDownDetails,也有可能是 DragUpdateDetails")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这两个类型没有父类,不过都有一个相同类型的 localPosition,存放了当前手指在列表上的偏移量,所以这里为了方便,类型声明为 dynamic")]),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("details "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("is!")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DragUpdateDetails")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" details "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("is!")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DragDownDetails")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n observeOffset "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" details"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("localPosition"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("dy"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 触发一次观察")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 通过 await 就可以在该处拿到观察结果")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" 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(" observerController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("dispatchOnceObserve")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 在上面的 ListViewObserver 中,我们并没有实现 onObserve 和 onObserveAll 回调")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 在默认情况下,如果回调没有实现,则不会去观察,所以在这里设置了不依赖回调的实现,直接观察")]),t._v("\n isDependObserveCallback"),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(",")]),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(" observeResult "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" result"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("observeResult"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 观察结果为null,说明此次的 第一个item 没有发生变化,比如:上一次观察时为字母A,此次观察还是字母A")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 默认内部对观察结果有做对比处理,如果你希望每次观察不用对比,直接将数据返回,则可以在上述的 dispatchOnceObserve 方法中给 isForce 设置为 true")]),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("observeResult "),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 keyword"}},[t._v("return")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 取出 第一个item 的数据")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" firstChildModel "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" observeResult"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("firstChild"),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("firstChildModel "),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 keyword"}},[t._v("return")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 第一个item的下标")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" firstChildIndex "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" firstChildModel"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 取出字母视图对应的 RenderObject")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" firstChildRenderObj "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" firstChildModel"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("renderObject"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 计算当前字母的中心点相对于父视图左上角的偏移量,我们主要拿 y 值")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ancestor: 传入祖先视图的 RenderObject 做为参考坐标系")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" firstChildRenderObjOffset "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" firstChildRenderObj"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("localToGlobal")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Offset")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("zero"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n ancestor"),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("parentKey"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("currentContext"),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("findRenderObject")]),s("span",{pre:!0,attrs:{class:"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 comment"}},[t._v("// 计算好后,通过 onSelectionUpdate 回调将数据返出去")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" cursorOffset "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Offset")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n firstChildRenderObjOffset"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("dx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n firstChildRenderObjOffset"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("dy "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" firstChildModel"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("size"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("width "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0.5")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 widget"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("onSelectionUpdate"),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("call")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n firstChildIndex"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n cursorOffset"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("h3",{attrs:{id:"_4、更新游标"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_4、更新游标"}},[t._v("#")]),t._v(" 4、更新游标")]),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 comment"}},[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("AzListCursorInfoModel")]),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("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" title"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n \n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 字母中心点的偏移量")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Offset")]),t._v(" offset"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AzListCursorInfoModel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n required "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("this")]),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 required "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("this")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("offset"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("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 class-name"}},[t._v("ValueNotifier")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AzListCursorInfoModel")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" cursorInfo "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ValueNotifier")]),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(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildIndexBar")]),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 class-name"}},[t._v("Container")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 child"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AzListIndexBar")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n parentKey"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" indexBarContainerKey"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n symbols"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" symbols"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n onSelectionUpdate"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" cursorOffset"),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 cursorInfo"),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 class-name"}},[t._v("AzListCursorInfoModel")]),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(" symbols"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),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(" cursorOffset"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 comment"}},[t._v("// 取出字母对应的联系人列表视图 SliverList 的 BuildContext")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" sliverContext "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" sliverContextMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("index"),s("span",{pre:!0,attrs:{class:"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("sliverContext "),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 keyword"}},[t._v("return")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 跳到对应的字母章节的第一个 item 的位置")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// jumpTo 方法内部处理了跳动问题,尽管调用即可!")]),t._v("\n observerController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("jumpTo")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n index"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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 sliverContext"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" sliverContext"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 onSelectionEnd"),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 comment"}},[t._v("// 清除游标数据,即隐藏游标")]),t._v("\n cursorInfo"),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 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("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("构建游标视图代码")]),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 class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildCursor")]),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("// 根据 cursorInfo 数据的变化来局部实现")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ValueListenableBuilder")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AzListCursorInfoModel")]),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("(")]),t._v("\n valueListenable"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" cursorInfo"),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("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildContext")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AzListCursorInfoModel")]),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 class-name"}},[t._v("Widget")]),s("span",{pre:!0,attrs:{class:"token operator"}},[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(")")]),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("Widget")]),t._v(" resultWidget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Container")]),s("span",{pre:!0,attrs:{class:"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 double top "),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 double right "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" indexBarWidth "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("8")]),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("value "),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 "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 没有游标数据,隐藏")]),t._v("\n resultWidget "),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 class-name"}},[t._v("SizedBox")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("shrink")]),s("span",{pre:!0,attrs:{class:"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 comment"}},[t._v("// 有游标数据,显示 AzListCursor 视图")]),t._v("\n double titleSize "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("80")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 根据当前字母视图的中心点偏移量 y 值,减去游标视图的高度,来得出游标的顶部偏移量")]),t._v("\n top "),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("offset"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("dy "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v(" titleSize "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0.5")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n resultWidget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AzListCursor")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" titleSize"),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(" value"),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("}")]),t._v("\n resultWidget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Positioned")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n top"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" top"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n right"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" right"),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(" resultWidget"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("return")]),t._v(" resultWidget"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("游标视图 "),s("code",[t._v("AzListCursor")]),t._v(" 里没什么重要的内容,所以不讲解了,大家可以随意发挥。")]),t._v(" "),s("p",[t._v("Demo链接:"),s("a",{attrs:{href:"https://github.com/fluttercandies/flutter_scrollview_observer/tree/main/example/lib/features/scene/azlist_demo",target:"_blank",rel:"noopener noreferrer"}},[t._v("azlist_demo"),s("OutboundLink")],1)])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/137.d3bfeadd.js b/assets/js/137.d3bfeadd.js new file mode 100644 index 000000000..d6c2c7e9d --- /dev/null +++ b/assets/js/137.d3bfeadd.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[137],{449:function(t,s,a){"use strict";a.r(s);var e=a(8),r=Object(e.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[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("Flutter")]),t._v(" 官方文档 "),s("a",{attrs:{href:"https://docs.flutter.dev/add-to-app/android/project-setup?tab=module-source",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://docs.flutter.dev/add-to-app/android/project-setup?tab=module-source"),s("OutboundLink")],1),t._v(",以源码依赖的方式集成到原生项目,在集成 "),s("code",[t._v("Shorebird")]),t._v(" 实现热更新时,需要我们将源码依赖的方式调整成二进制依赖。")]),t._v(" "),s("p",[t._v("安卓端的改动还是很简单的,一路下来没怎么碰壁,接下来一起来看看如何操作吧")]),t._v(" "),s("h2",{attrs:{id:"二、shorebird-初始化"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#二、shorebird-初始化"}},[t._v("#")]),t._v(" 二、Shorebird 初始化")]),t._v(" "),s("p",[t._v("本文以 "),s("code",[t._v("Mac")]),t._v(" 环境为例,如果你是 "),s("code",[t._v("Windows")]),t._v(" 用户,请按官方文档去操作: "),s("a",{attrs:{href:"https://docs.shorebird.dev",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://docs.shorebird.dev"),s("OutboundLink")],1)]),t._v(" "),s("blockquote",[s("p",[t._v("该部分内容只需要一开始没有环境和相应的文件时去操作一次")])]),t._v(" "),s("h3",{attrs:{id:"安装-cli"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#安装-cli"}},[t._v("#")]),t._v(" 安装 CLI")]),t._v(" "),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("curl")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--proto")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'=https'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--tlsv1.2")]),t._v(" https://raw.githubusercontent.com/shorebirdtech/install/main/install.sh "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-sSf")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("bash")]),t._v("\n")])])]),s("p",[t._v("重启终端,输入 "),s("code",[t._v("shorebird")]),t._v(" 即可看到相应的命令输出")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("$ shorebird\nThe shorebird command-line tool\n\nUsage: shorebird "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("command"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("arguments"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n\nGlobal options:\n-h, "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--help")]),t._v(" Print this usage information.\n "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--version")]),t._v(" Print the current version.\n-v, --"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("no-"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("verbose Noisy logging, including all shell commands executed.\n\nAvailable commands:\n cache Manage the Shorebird cache.\n doctor Show information about the installed tooling.\n flutter Manage your Shorebird Flutter installation.\n init Initialize Shorebird.\n login Login as a new Shorebird user.\n login:ci Login as a CI user.\n "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("logout")]),t._v(" Logout of the current Shorebird user\n patch Manage patches "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" a specific release "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" Shorebird.\n preview Preview a specific release on a device.\n release Manage your Shorebird app releases.\n upgrade Upgrade your copy of Shorebird.\n\nRun "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"shorebird help "')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("more")]),t._v(" information about a command.\n")])])]),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("Shorebird")]),t._v(" 账号")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("shorebird login\n")])])]),s("h3",{attrs:{id:"选择-flutter-版本"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#选择-flutter-版本"}},[t._v("#")]),t._v(" 选择 "),s("code",[t._v("Flutter")]),t._v(" 版本")]),t._v(" "),s("p",[t._v("你可以查看当前 "),s("code",[t._v("Shorebird")]),t._v(" 支持哪些 "),s("code",[t._v("Flutter")]),t._v(" 版本")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("shorebird flutter versions list\n")])])]),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("$ shorebird flutter versions list\n📦 Flutter Versions\n✓ 3.13.9\n 3.13.8\n 3.13.7\n 3.13.6\n 3.13.5\n 3.13.4\n 3.13.3\n 3.13.2\n 3.13.1\n 3.13.0\n 3.10.7\n 3.10.6\n 3.10.5\n 3.10.4\n 3.10.3\n 3.10.2\n 3.10.1\n 3.10.0\n")])])]),s("p",[t._v("如我现在需要用到 "),s("code",[t._v("Flutter 3.13.9")]),t._v(" 版本,则输入如下命令")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("$ shorebird flutter versions use "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("3.13")]),t._v(".9\n")])])]),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("Flutter")]),t._v(" 项目下执行如下命令")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("shorebird init\n")])])]),s("p",[t._v("该命令会生成在项目根目录生成 "),s("code",[t._v("shorebird.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 comment"}},[t._v("# shorebird.ymal的内容")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("app_id")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" xxxxxxxx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("xxxx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("xxxx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("xxxx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v("xxxxxxxxxx\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# auto_update: false")]),t._v("\n")])])]),s("p",[t._v("以及修改 "),s("code",[t._v("pubspec.yaml")])]),t._v(" "),s("div",{staticClass:"language-yaml extra-class"},[s("pre",{pre:!0,attrs:{class:"language-yaml"}},[s("code",[t._v(" "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("assets")]),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(" shorebird.yaml\n")])])]),s("h2",{attrs:{id:"三、原生项目改造"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#三、原生项目改造"}},[t._v("#")]),t._v(" 三、原生项目改造")]),t._v(" "),s("blockquote",[s("p",[t._v("以下的相关修改是以我们项目来写的("),s("code",[t._v(".kts")]),t._v("),如果你的跟我的不一样,可以看一下文档:"),s("a",{attrs:{href:"https://docs.shorebird.dev/guides/hybrid-app/android",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://docs.shorebird.dev/guides/hybrid-app/android"),s("OutboundLink")],1)])]),t._v(" "),s("h3",{attrs:{id:"build-gradle-kts"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#build-gradle-kts"}},[t._v("#")]),t._v(" "),s("code",[t._v("build.gradle.kts")])]),t._v(" "),s("p",[t._v("新增本地二进制依赖")]),t._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[t._v("buildscript {\n"),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" repositories {\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" ...\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" \n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // 本地依赖Flutter包\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(' maven(url = "../../flutter_module/release")\n')]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(' maven(url = "https://download.shorebird.dev/download.flutter.io")\n')])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" \n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" ...\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" }\n")])]),t._v("}\n\nallprojects {\n"),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" repositories {\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" ...\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" \n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // 本地依赖Flutter包\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(' maven(url = "../../flutter_module/release")\n')]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(' maven(url = "https://download.shorebird.dev/download.flutter.io")\n')])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" \n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" ...\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" }\n")])]),t._v("}\n")])])]),s("h3",{attrs:{id:"settings-gradle-kts"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#settings-gradle-kts"}},[t._v("#")]),t._v(" "),s("code",[t._v("settings.gradle.kts")])]),t._v(" "),s("p",[t._v("注释掉本地源码依赖")]),t._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" evaluate(new File(\n")]),s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" settingsDir,\n")]),s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(' "../flutter_module/.android/include_flutter.groovy"\n')]),s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" ))\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" //evaluate(new File(\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // settingsDir,\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(' // "../flutter_module/.android/include_flutter.groovy"\n')]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" //))\n")])])])])]),s("h3",{attrs:{id:"app-build-gradle-kts"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#app-build-gradle-kts"}},[t._v("#")]),t._v(" "),s("code",[t._v("app/build.gradle.kts")])]),t._v(" "),s("p",[t._v("注释掉本地源码依赖,新增本地二进制依赖")]),t._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[t._v("dependencies {\n"),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // Flutter源码依赖\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" implementation(project(LocalLib.flutter)) \n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // implementation(project(LocalLib.flutter))\n")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" // Flutter二进制依赖\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(' releaseImplementation("com.lxf.flutter_modules:flutter_release:1.0")\n')])]),t._v("}\n")])])]),s("h2",{attrs:{id:"四、创建-shorebird-release"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#四、创建-shorebird-release"}},[t._v("#")]),t._v(" 四、创建 Shorebird Release")]),t._v(" "),s("p",[t._v("打发布包的时候操作,在 "),s("code",[t._v("Flutter")]),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 builtin class-name"}},[t._v("cd")]),t._v(" xx/xx/flutter_modules\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 7.0.0+320: 大版本号+小版本号")]),t._v("\nshorebird release aar --release-version "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("7.0")]),t._v(".0+320\n")])])]),s("blockquote",[s("p",[t._v("该命令内部会去执行 "),s("code",[t._v("flutter build aar --no-debug --no-profile ...")]),t._v(",并且使用的是 "),s("code",[t._v("Shorebird")]),t._v(" 魔改的 "),s("code",[t._v("Flutter")]),t._v(" 引擎!")])]),t._v(" "),s("p",[t._v("版本号可以在如下路径的文件中查看")]),t._v(" "),s("div",{staticClass:"language-xml extra-class"},[s("pre",{pre:!0,attrs:{class:"language-xml"}},[s("code",[t._v("# 路径: app/src/main/AndroidManifest.xml\n# 小版本号: android:versionCode\n# 大版本号: android:versionName\n\n"),s("span",{pre:!0,attrs:{class:"token prolog"}},[t._v('')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("manifest")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("xmlns:")]),t._v("android")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("http://schemas.android.com/apk/res/android"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("xmlns:")]),t._v("tools")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("http://schemas.android.com/tools"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("package")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("com.lxf.flutter_modules"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("android:")]),t._v("versionCode")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("320"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("android:")]),t._v("versionName")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("7.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...\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])]),s("p",[s("code",[t._v("ShoreBird")]),t._v(" 的内部逻辑会去以这个版本号组合,向服务器请求判断是否存在相应版本的相关补丁!")]),t._v(" "),s("p",[t._v("执行完成后,在 "),s("code",[t._v("Shorebird")]),t._v(" 控制台上可以看到相应的项")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202401071836372.png",alt:""}})]),t._v(" "),s("p",[t._v("在命令执行前,请确保不存在 "),s("code",[t._v("7.0.0+320")]),t._v(" 的 "),s("code",[t._v("Release")]),t._v(",如果有的话,请先删除")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202401071836142.png",alt:""}})]),t._v(" "),s("h2",{attrs:{id:"五、创建-shorebird-patch"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#五、创建-shorebird-patch"}},[t._v("#")]),t._v(" 五、创建 Shorebird Patch")]),t._v(" "),s("p",[t._v("紧急修复线上包的bug时操作,在 "),s("code",[t._v("Flutter")]),t._v(" 工程目录下执行")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("shorebird patch aar --release-version "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("7.0")]),t._v(".0+320\n")])])]),s("p",[t._v("注:版本号与上述的 "),s("code",[t._v("release")]),t._v(" 命令中使用的要保持一致!")]),t._v(" "),s("p",[t._v("执行完成后,在 "),s("code",[t._v("Shorebird")]),t._v(" 控制台上点击对应的 "),s("code",[t._v("Release")]),t._v(" 项,进去后可以看到相应的补丁")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202401071837938.png",alt:""}})]),t._v(" "),s("h2",{attrs:{id:"六、热更新验证"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#六、热更新验证"}},[t._v("#")]),t._v(" 六、热更新验证")]),t._v(" "),s("ol",[s("li",[t._v("未打 "),s("code",[t._v("patch")]),t._v(" 时,请以 "),s("code",[t._v("release")]),t._v(" 模式运行原生工程(在 "),s("code",[t._v("Active Build Variant")]),t._v(" 的下拉选项中选择 "),s("code",[t._v("release")]),t._v(")")]),t._v(" "),s("li",[t._v("关闭 "),s("code",[t._v("App")])]),t._v(" "),s("li",[t._v("打 "),s("code",[t._v("patch")]),t._v(" 后,重新打开 "),s("code",[t._v("App")]),t._v(",关闭 "),s("code",[t._v("App")]),t._v(" 再打开 "),s("code",[t._v("App")]),t._v(",即可看到变化")])]),t._v(" "),s("p",[t._v("如果补丁没有生效,可以在 "),s("code",[t._v("Logcat")]),t._v(" 内检测相关输出,成功还是失败可以直接看后两句,如下两种情况的输出:")]),t._v(" "),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 punctuation"}},[t._v("[")]),t._v("INFO:shorebird.cc"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("109")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Shorebird updater: no active patch.\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("INFO:shorebird.cc"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("113")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Starting Shorebird update\nupdater::network: Sending patch check request: PatchCheckRequest "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" app_id: "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx"')]),t._v(", channel: "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"stable"')]),t._v(", release_version: "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"7.0.0+320"')]),t._v(", patch_number: None, platform: "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"android"')]),t._v(", arch: "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"aarch64"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\nupdater::updater: Patch "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" successfully installed.\nupdater::updater: Update thread finished with status: Update installed\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 punctuation"}},[t._v("[")]),t._v("INFO:shorebird.cc"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("109")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Shorebird updater: no active patch.\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("INFO:shorebird.cc"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("113")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Starting Shorebird update\nupdater::network: Sending patch check request: PatchCheckRequest "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" app_id: "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx"')]),t._v(", channel: "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"stable"')]),t._v(", release_version: "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"7.0.0+320"')]),t._v(", patch_number: None, platform: "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"android"')]),t._v(", arch: "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"aarch64"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\nupdater::updater: Update failed: failed to fill whole buffer\nupdater::updater: Update thread finished with status: Update had error\n")])])]),s("p",[t._v("失败的时候,看一下 "),s("code",[t._v("PatchCheckRequest")]),t._v(" 后面的请求参数输出,一般失败都是因为这里的参数错误才导致。")]),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("Python")]),t._v(" 去制作了简易的脚本,并结合 "),s("code",[t._v("Jenkins")]),t._v(" 来辅助完成这种万年不变的无聊步骤")]),t._v(" "),s("p",[t._v("脚本已上传至 "),s("code",[t._v("Github")]),t._v(": "),s("a",{attrs:{href:"https://github.com/LinXunFeng/script_box/tree/main/flutter",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/LinXunFeng/script_box/tree/main/flutter"),s("OutboundLink")],1)]),t._v(" "),s("p",[t._v("看官可自取修改~")]),t._v(" "),s("h3",{attrs:{id:"switch-flutter-integrate-py"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#switch-flutter-integrate-py"}},[t._v("#")]),t._v(" switch_flutter_integrate.py")]),t._v(" "),s("blockquote",[s("p",[t._v("切换 "),s("code",[t._v("Flutter")]),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 comment"}},[t._v("# 二进制依赖")]),t._v("\npython switch_flutter_integrate.py "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-p")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'原生工程路径'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-m")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'binary'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'android'")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 源码依赖")]),t._v("\npython switch_flutter_integrate.py "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-p")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'原生工程路径'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-m")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'source'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'android'")]),t._v(" \n")])])]),s("h3",{attrs:{id:"shorebird-py"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#shorebird-py"}},[t._v("#")]),t._v(" shorebird.py")]),t._v(" "),s("blockquote",[s("p",[t._v("自动获取版本号,并执行 "),s("code",[t._v("Shorebird")]),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 comment"}},[t._v("# release")]),t._v("\npython shorebird.py "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-p")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'原生工程路径'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-s")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Flutter工程路径'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-m")]),t._v(" release "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" android\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# patch")]),t._v("\npython shorebird.py "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-p")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'原生工程路径'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-s")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Flutter工程路径'")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-m")]),t._v(" patch "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" android\n")])])])])}),[],!1,null,null,null);s.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/138.31c4d124.js b/assets/js/138.31c4d124.js new file mode 100644 index 000000000..05e13eaa9 --- /dev/null +++ b/assets/js/138.31c4d124.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[138],{450:function(t,e,s){"use strict";s.r(e);var a=s(8),r=Object(a.a)({},(function(){var t=this,e=t._self._c;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("关于 "),e("code",[t._v("Shorebird")]),t._v(" 的初始化内容可以在上一篇《"),e("a",{attrs:{href:"/pages/a0e176"}},[t._v("Flutter - 混编项目集成Shorebird热更新🐦(安卓篇)")]),t._v("》中查看,这里就不再赘述了。")]),t._v(" "),e("p",[e("code",[t._v("Shorebird")]),t._v(" 官方文档上对于 "),e("code",[t._v("iOS")]),t._v(" 混编方案集成热更新的介绍不算详细,只能说点明了要点,指明了方向。")]),t._v(" "),e("p",[t._v("本文将根据实际的项目应用情况做出集成调整,并补充说明正确的补丁验证方案。")]),t._v(" "),e("h2",{attrs:{id:"二、踩坑"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#二、踩坑"}},[t._v("#")]),t._v(" 二、踩坑")]),t._v(" "),e("p",[e("code",[t._v("Shorebird")]),t._v(" 文档里指出需要我们使用类似 "),e("code",[t._v("Flutter")]),t._v(" 官方文档里 "),e("code",[t._v("Option B - Embed frameworks in Xcode")]),t._v(" 的方式去集成 "),e("code",[t._v("Flutter")]),t._v(" 模块。")]),t._v(" "),e("blockquote",[e("p",[t._v("关于 "),e("code",[t._v("Flutter")]),t._v(" 官方文档指出的各种集成方式可以查看: "),e("a",{attrs:{href:"https://docs.flutter.dev/add-to-app/ios/project-setup",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://docs.flutter.dev/add-to-app/ios/project-setup"),e("OutboundLink")],1)])]),t._v(" "),e("p",[t._v("具体的步骤就是:")]),t._v(" "),e("ol",[e("li",[t._v("先注释掉原来的 "),e("code",[t._v("Option A")]),t._v(" 集成方式的相关配置代码")]),t._v(" "),e("li",[t._v("执行 "),e("code",[t._v("Shorebird Release")]),t._v(" 去构建对应的所有 "),e("code",[t._v("xcframework")]),t._v(" 文件。("),e("code",[t._v("xcframework")]),t._v(" 文件包括了 "),e("code",[t._v("Flutter.xcframework")]),t._v(" 和 "),e("code",[t._v("App.xcframework")]),t._v(",以及插件依赖的原生第三方库对应的 "),e("code",[t._v("xcframework")]),t._v(")")]),t._v(" "),e("li",[t._v("将所有构建完成的 "),e("code",[t._v("xcframework")]),t._v(" 拖到 "),e("code",[t._v("Build Phases")]),t._v(" 中的 "),e("code",[t._v("Embed Frameworks")]),t._v(" 内。")]),t._v(" "),e("li",[t._v("将 "),e("code",[t._v("xcframework")]),t._v(" 的所在目录路径配置到 "),e("code",[t._v("Framework Search Paths")]),t._v("。")]),t._v(" "),e("li",[t._v("配置 "),e("code",[t._v("xcframework")]),t._v(" 的 "),e("code",[t._v("Embed")]),t._v(" 模式,静态库必须选 "),e("code",[t._v("Do Not Embed")]),t._v(",动态库必须选 "),e("code",[t._v("Embed & Sign")]),t._v("。")])]),t._v(" "),e("p",[t._v("因为 "),e("code",[t._v("Option A - Embed with CocoaPods and the Flutter SDK")]),t._v(" 的方式只需要简单的配置 "),e("code",[t._v("Podfile")]),t._v(" 就可以集成 "),e("code",[t._v("Flutter")]),t._v(" 模块,所以相信大家在一般情况下都是会选择 "),e("code",[t._v("Option A")]),t._v(" 的方式。很明显,要改成 "),e("code",[t._v("Option B")]),t._v(" 需要我们大改特改。")]),t._v(" "),e("p",[t._v("改成 "),e("code",[t._v("Option B")]),t._v(" 这种方式有以下几点问题:")]),t._v(" "),e("h3",{attrs:{id:"_1、vendored-frameworks-缺失"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_1、vendored-frameworks-缺失"}},[t._v("#")]),t._v(" 1、"),e("code",[t._v("vendored_frameworks")]),t._v(" 缺失")]),t._v(" "),e("p",[t._v("如果你依赖的 "),e("code",[t._v("Flutter")]),t._v(" 插件依赖了原生第三方的二进制包,如 "),e("code",[t._v("realm")]),t._v(",在它的 "),e("code",[t._v("podspec")]),t._v(" 文件是这样声明的 "),e("code",[t._v("s.vendored_frameworks = 'realm_dart.xcframework'")]),t._v(",那你会发现在最终构建完成的 "),e("code",[t._v("xcframework")]),t._v(" 的目录里会缺少这些 "),e("code",[t._v("vendored_frameworks")]),t._v("。")]),t._v(" "),e("p",[t._v("相关的 "),e("code",[t._v("issue")]),t._v(": "),e("a",{attrs:{href:"https://github.com/flutter/flutter/issues/125530",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/flutter/flutter/issues/125530"),e("OutboundLink")],1),t._v("。")]),t._v(" "),e("p",[t._v("因为 "),e("code",[t._v("Option B")]),t._v(" 是二进制依赖,所以在编译的时候并不会报任何错误,等你 "),e("code",[t._v("App")]),t._v(" 运行起来进入一些相关场景,使用到了对应的第三方功能时就会直接来个找不到符号的错误,如:")]),t._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[t._v("Failed to lookup symbol "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'native_method_signature'")]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" dlsym"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("0xa47e7c10, native_method_signature"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(": symbol not found\n")])])]),e("p",[t._v("接着就是闪退,可想而知这得多吓人!")]),t._v(" "),e("h3",{attrs:{id:"_2、重复编译"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_2、重复编译"}},[t._v("#")]),t._v(" 2、重复编译")]),t._v(" "),e("p",[e("code",[t._v("vendored_frameworks")]),t._v(" 缺失的问题我通过脚本解决了,但是还有另一个问题,这些 "),e("code",[t._v("xcframework")]),t._v(" 中也有可能出现涵盖你原来的原生工程里依赖的第三方包,比如,"),e("code",[t._v("Flutter")]),t._v(" 的插件用到了 "),e("code",[t._v("FMDB")]),t._v(",生成的 "),e("code",[t._v("xcframework")]),t._v(" 中就会包含 "),e("code",[t._v("FMDB.xcframework")]),t._v(",而你的原生工程本来就有依赖 "),e("code",[t._v("FMDB")]),t._v(",这个时候编译,"),e("code",[t._v("Xcode")]),t._v(" 就会告诉你重复了,编译不通过,报错内容如下:")]),t._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[t._v("Showing Recent Messages\nMultiple commands produce "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'/Users/lxf/Library/Developer/Xcode/DerivedData/xxx.app/Frameworks/FMDB.framework'")]),t._v("\n")])])]),e("p",[t._v("如果是你,你选择留下哪个呢?")]),t._v(" "),e("ul",[e("li",[t._v("如果你选择了 "),e("code",[t._v("Flutter")]),t._v(" 帮你生成的 "),e("code",[t._v("FMDB.xcframework")]),t._v(",你就得去处理其它原生第三方依赖的 "),e("code",[t._v("pod 'FMDB'")]),t._v(",假如此时原生工程里的一些第三方库或私有库也依赖 "),e("code",[t._v("FMDB")]),t._v(",那你要处理这些库可就太麻烦了。")]),t._v(" "),e("li",[t._v("如果你选择使用 "),e("code",[t._v("pod 'FMDB'")]),t._v(" 的方式,那你只需要去判断原生工程里是否有对应的依赖,有的话就不再声明依赖,这种还好。")])]),t._v(" "),e("h3",{attrs:{id:"_3、静态库与动态库"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_3、静态库与动态库"}},[t._v("#")]),t._v(" 3、静态库与动态库")]),t._v(" "),e("p",[t._v("生成的 "),e("code",[t._v("xcframework")]),t._v(" 中,有些是静态库,有些是动态库")]),t._v(" "),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202401211435264.png",alt:""}})]),t._v(" "),e("p",[t._v("如图所示,静态库必须选 "),e("code",[t._v("Do Not Embed")]),t._v(",动态库必须选 "),e("code",[t._v("Embed & Sign")]),t._v("。")]),t._v(" "),e("p",[t._v("如果你全选了 "),e("code",[t._v("Embed & Sign")]),t._v(",那么你就无法启动 "),e("code",[t._v("App")]),t._v(" 了,如下图所示")]),t._v(" "),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202401211436641.png",alt:""}})]),t._v(" "),e("p",[t._v("该问题的相关 "),e("code",[t._v("issue")]),t._v(": https://github.com/flutter/flutter/issues/122183")]),t._v(" "),e("p",[t._v("所以为了避免这种情况,我们就必须得选对 "),e("code",[t._v("Embed")]),t._v(" 选项,可以使用 "),e("code",[t._v("file")]),t._v(" 命令去判断 "),e("code",[t._v("xcframework")]),t._v(" 是静态库还是动态库")]),t._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 function"}},[t._v("file")]),t._v(" FlutterPluginRegistrant.xcframework/ios-arm64/FlutterPluginRegistrant.framework/FlutterPluginRegistrant\nFlutterPluginRegistrant.xcframework/ios-arm64/FlutterPluginRegistrant.framework/FlutterPluginRegistrant: \ncurrent ar archive random library // 静态库\n\n"),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("file")]),t._v(" url_launcher_ios.xcframework/ios-arm64/url_launcher_ios.framework/url_launcher_ios\nurl_launcher_ios.xcframework/ios-arm64/url_launcher_ios.framework/url_launcher_ios: \nMach-O "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("64")]),t._v("-bit dynamically linked shared library arm64 // 动态库\n")])])]),e("p",[t._v("这部分判断逻辑只能交给脚本处理了,因为当数量起来后你就会体验到什么叫崩溃,别问我是怎么知道的 😭")]),t._v(" "),e("h3",{attrs:{id:"_4、直接崩溃"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_4、直接崩溃"}},[t._v("#")]),t._v(" 4、直接崩溃")]),t._v(" "),e("p",[t._v("后面我直接用脚本判断 "),e("code",[t._v("Flutter")]),t._v(" 插件依赖了哪些原生第三方,将它们统一在原生工程内声明依赖,在一些情况下这也是很危险的,如 "),e("code",[t._v("connectivity_plus")]),t._v(" 这个 "),e("code",[t._v("Flutter")]),t._v(" 插件依赖了 "),e("code",[t._v("ReachabilitySwift")]),t._v(",你必须得使用 "),e("code",[t._v("Reachability.xcframework")]),t._v(" 二进制嵌入的方式,否则运行就崩~")]),t._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[t._v("dyld"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("31764")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(": Symbol not found: _"),e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$s12ReachabilityAAC10ConnectionO4wifiyA2DmFWC")]),t._v("\n Referenced from: "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("8142F86E-4C9C-3513-AD29-D3522FC6677F"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" /Users/lxf/Library/Developer/Xcode/DerivedData/xxx/connectivity_plus.framework/connectivity_plus\n Expected in: "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("DA318000-9A97-35AD-87EA-7C5B635DE01"),e("span",{pre:!0,attrs:{class:"token operator"}},[e("span",{pre:!0,attrs:{class:"token file-descriptor important"}},[t._v("0")]),t._v(">")]),t._v(" /Users/lxf/Library/Developer/xxx.app/Frameworks/Reachability.framework/Reachability\n")])])]),e("h2",{attrs:{id:"三、分析"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#三、分析"}},[t._v("#")]),t._v(" 三、分析")]),t._v(" "),e("p",[t._v("后来仔细想想,"),e("code",[t._v("Shorebird")]),t._v(" 的热更新是针对 "),e("code",[t._v("Dart")]),t._v(" 代码,跟原生无关,能不能按原来的 "),e("code",[t._v("Cocoapods")]),t._v(" 方式去集成 "),e("code",[t._v("Flutter.xcframework")]),t._v(","),e("code",[t._v("App.xcframework")]),t._v(" 以及插件依赖的原生第三方库呢?")]),t._v(" "),e("p",[t._v("答案是可以的,来看看 "),e("code",[t._v("install_all_flutter_pods")]),t._v(" 方法")]),t._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 keyword"}},[t._v("def")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token method-definition"}},[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("install_all_flutter_pods")])]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("flutter_application_path "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("nil")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),t._v("\n flutter_application_path "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("||=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("File")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("join"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token string-literal"}},[e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'..'")])]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string-literal"}},[e("span",{pre:!0,attrs:{class:"token string"}},[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("# 生成 .ios/Flutter/Flutter.podspec")]),t._v("\n install_flutter_engine_pod"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("flutter_application_path"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 集成 插件依赖的原生库 Pods")]),t._v("\n install_flutter_plugin_pods"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("flutter_application_path"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 编译并集成 Flutter.xcframework 和 App.xcframework")]),t._v("\n install_flutter_application_pod"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("flutter_application_path"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("end")]),t._v("\n")])])]),e("h3",{attrs:{id:"_1、install-flutter-engine-pod"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_1、install-flutter-engine-pod"}},[t._v("#")]),t._v(" 1、install_flutter_engine_pod")]),t._v(" "),e("p",[e("code",[t._v("install_flutter_engine_pod")]),t._v(" 生成的 "),e("code",[t._v("Flutter.podspec")]),t._v(" 是假的"),e("code",[t._v("podspec")]),t._v(",里面没啥实质内容,仅代表 "),e("code",[t._v("Flutter.xcframework")]),t._v(",为什么要这么做呢?因为一些 "),e("code",[t._v("Flutter")]),t._v(" 插件声明需要依赖 "),e("code",[t._v("Flutter")]),t._v(",如:")]),t._v(" "),e("div",{staticClass:"language-ruby extra-class"},[e("pre",{pre:!0,attrs:{class:"language-ruby"}},[e("code",[t._v("Pod"),e("span",{pre:!0,attrs:{class:"token double-colon punctuation"}},[t._v("::")]),e("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Spec")]),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 keyword"}},[t._v("do")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v("s"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v("\n s"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string-literal"}},[e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'sqflite'")])]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),t._v("\n s"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("dependency "),e("span",{pre:!0,attrs:{class:"token string-literal"}},[e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Flutter'")])]),t._v("\n s"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("dependency "),e("span",{pre:!0,attrs:{class:"token string-literal"}},[e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'FMDB'")])]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string-literal"}},[e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'>= 2.7.5'")])]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("end")]),t._v("\n")])])]),e("p",[t._v("如果没有这个 "),e("code",[t._v("Flutter.podspec")]),t._v(",那么执行 "),e("code",[t._v("pod install")]),t._v(" 就会从 "),e("code",[t._v("CocoaPods trunk")]),t._v(" 下载 "),e("code",[t._v("Flutter")]),t._v(" 了。")]),t._v(" "),e("h3",{attrs:{id:"_2、install-flutter-application-pod"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_2、install-flutter-application-pod"}},[t._v("#")]),t._v(" 2、install_flutter_application_pod")]),t._v(" "),e("p",[e("code",[t._v("install_flutter_application_pod")]),t._v(" 会去编译 "),e("code",[t._v("Flutter.xcframework")]),t._v(" 和 "),e("code",[t._v("App.xcframework")]),t._v(",并将它们并集到我们的原生工程内。不过这两玩意我们用 "),e("code",[t._v("Shorebird Release")]),t._v(" 去生成了,所以这个方法我们用不上。")]),t._v(" "),e("p",[t._v("我们可以结合上述的 "),e("code",[t._v("Flutter.podspec")]),t._v(" 的作用,修改它内部的依赖声明,从而实现通过 "),e("code",[t._v("Cocoapods")]),t._v(" 的方式来集成 "),e("code",[t._v("Flutter.xcframework")]),t._v(" 和 "),e("code",[t._v("App.xcframework")]),t._v("。")]),t._v(" "),e("div",{staticClass:"language-diff extra-class"},[e("pre",{pre:!0,attrs:{class:"language-diff"}},[e("code",[t._v("Pod::Spec.new do |s|\ns.name = 'Flutter'\ns.version = '1.0.0'\ns.summary = 'A UI toolkit for beautiful and fast apps.'\ns.homepage = 'https://flutter.dev'\ns.license = { :type => 'BSD' }\ns.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' }\ns.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s }\ns.ios.deployment_target = '11.0'\n# Framework linking is handled by Flutter tooling, not CocoaPods.\n# Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs.\n#\n# 以上到这句都是原来的,将这句注释掉\n"),e("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[e("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" # s.vendored_frameworks = 'path/to/nothing'\n")])]),t._v("# 新增下面这句,声明依赖当前目录下的 Flutter.xcframework 和 App.xcframework\n"),e("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[e("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" s.vendored_frameworks = 'Flutter.xcframework', 'App.xcframework'\n")])]),t._v("end\n")])])]),e("p",[t._v("生成的所有 "),e("code",[t._v("xcframework")]),t._v(" 所在路径为: "),e("code",[t._v("xxx/flutter_module/build/ios/framework/Release")]),t._v(", 我们自己创建的 "),e("code",[t._v("Flutter.podspec")]),t._v(" 中的依赖是相对路径,所以该 "),e("code",[t._v("podspec")]),t._v(" 也是跟 "),e("code",[t._v("xcframework")]),t._v(" 放到一起,当然也可以根据你自己的习惯进行调整。")]),t._v(" "),e("h3",{attrs:{id:"_3、install-flutter-plugin-pods"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_3、install-flutter-plugin-pods"}},[t._v("#")]),t._v(" 3、install_flutter_plugin_pods")]),t._v(" "),e("p",[e("code",[t._v("install_flutter_plugin_pods")]),t._v(" 会将 "),e("code",[t._v("Flutter")]),t._v(" 插件依赖的原生库集成到我们的原生工程,这正是我们需要的。")]),t._v(" "),e("p",[t._v("不过如果你直接将 "),e("code",[t._v("Podfile")]),t._v(" 中的 "),e("code",[t._v("install_flutter_application_pod")]),t._v(" 给替换成 "),e("code",[t._v("install_flutter_plugin_pods")]),t._v(" ,执行 "),e("code",[t._v("pod install")]),t._v(" 时是会报如下错误的:")]),t._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[t._v("pod "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("install")]),t._v("\n\n"),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 punctuation"}},[t._v("]")]),t._v(" Invalid "),e("span",{pre:!0,attrs:{class:"token variable"}},[e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("`")]),t._v("Podfile"),e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("`")])]),t._v(" file: undefined method `flutter_relative_path_from_podfile' "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("#, @internal_hash={}, @root_target_definitions=[#], @current_target_definition=#>")]),t._v("\n\n relative "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" flutter_relative_path_from_podfile"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("export_script_directory"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),e("p",[t._v("也就是找不到 "),e("code",[t._v("flutter_relative_path_from_podfile")]),t._v(" 方法,因为该方法在并不在你的 "),e("code",[t._v("Flutter")]),t._v(" 模块的 "),e("code",[t._v("podhelper.rb")]),t._v(" 中,而是在 "),e("code",[t._v("packages/flutter_tools/bin/podhelper.rb")]),t._v("。")]),t._v(" "),e("p",[t._v("至于为什么原来的 "),e("code",[t._v("install_all_flutter_pods")]),t._v(" 方法不会报错,是因为在该方法内先引用了 "),e("code",[t._v("flutter_tools/bin/podhelper.rb")]),t._v("。")]),t._v(" "),e("p",[t._v("关键代码如下:")]),t._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 keyword"}},[t._v("def")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token method-definition"}},[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("install_all_flutter_pods")])]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("flutter_application_path "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("nil")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token operator"}},[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("require")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("File")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("expand_path"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("File")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("join"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token string-literal"}},[e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'packages'")])]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string-literal"}},[e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'flutter_tools'")])]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string-literal"}},[e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'bin'")])]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string-literal"}},[e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'podhelper'")])]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" flutter_root"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n flutter_application_path "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("||=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("File")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("join"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token string-literal"}},[e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'..'")])]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string-literal"}},[e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'..'")])]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n install_flutter_engine_pod"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("flutter_application_path"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n install_flutter_plugin_pods"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("flutter_application_path"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n install_flutter_application_pod"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("flutter_application_path"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("end")]),t._v("\n")])])]),e("p",[t._v("所以我们可以如法炮制,在 "),e("code",[t._v("install_flutter_plugin_pods")]),t._v(" 方法中加入 "),e("code",[t._v("require")]),t._v(" 这一行代码,以解决上述错误。")]),t._v(" "),e("div",{staticClass:"language-diff extra-class"},[e("pre",{pre:!0,attrs:{class:"language-diff"}},[e("code",[t._v("def install_flutter_plugin_pods(flutter_application_path)\n"),e("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[e("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)\n")])]),e("span",{pre:!0,attrs:{class:"token unchanged"}},[e("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" flutter_application_path ||= File.join('..', '..')\n")]),e("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" ...\n")])]),t._v("end\n\n")])])]),e("p",[t._v("老是这么改也不是个办法,所以我提了个 "),e("code",[t._v("PR")]),t._v(": https://github.com/flutter/flutter/pull/141521")]),t._v(" "),e("blockquote",[e("p",[t._v("该 "),e("code",[t._v("PR")]),t._v(" 现已合并,应该会在 "),e("code",[t._v("3.16.9")]),t._v(" 及之后的版本中生效。")])]),t._v(" "),e("p",[t._v("经过验证,该方案是可行的,下面我们来看看如何调整原生工程和 "),e("code",[t._v("Shorebird")]),t._v(" 在 "),e("code",[t._v("iOS")]),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("code",[t._v("Podfile")]),t._v(" 文件中,将 "),e("code",[t._v("Flutter")]),t._v(" 壳工程的源码依赖方式调整为二进制依赖")]),t._v(" "),e("div",{staticClass:"language-diff extra-class"},[e("pre",{pre:!0,attrs:{class:"language-diff"}},[e("code",[e("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[e("span",{pre:!0,attrs:{class:"token prefix deleted"}},[t._v("-")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" install_all_flutter_pods(flutter_application_path)\n")])]),e("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[e("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" # 源码集成\n")]),e("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" # install_all_flutter_pods(flutter_application_path)\n")])]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[e("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" # 二进制集成\n")]),e("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" pod 'Flutter', path: 'xxx/flutter_modules/build/ios/framework/Release'\n")]),e("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" install_flutter_plugin_pods(flutter_application_path)\n")])])])])]),e("ol",[e("li",[t._v("声明 "),e("code",[t._v("Flutter")]),t._v(" 依赖,用于集成 "),e("code",[t._v("Flutter.xcframework")]),t._v(" 和 "),e("code",[t._v("App.xcframework")]),t._v("。")]),t._v(" "),e("li",[e("code",[t._v("Option A")]),t._v(" 方式所需要的代码统统保留,只需要将 "),e("code",[t._v("install_all_flutter_pods")]),t._v(" 替换为 "),e("code",[t._v("install_flutter_plugin_pods")]),t._v(",用于集成 "),e("code",[t._v("Flutter")]),t._v(" 插件所依赖的原生第三方库")])]),t._v(" "),e("h2",{attrs:{id:"五、创建-shorebird-release"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#五、创建-shorebird-release"}},[t._v("#")]),t._v(" 五、创建 Shorebird Release")]),t._v(" "),e("p",[t._v("打发布包的时候操作,在 "),e("code",[t._v("Flutter")]),t._v(" 工程目录下执行")]),t._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"}},[t._v("cd")]),t._v(" xx/xx/flutter_modules\n\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 7.0.0+2: 版本号+build版本号")]),t._v("\nshorebird release ios-framework-alpha --release-version "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("7.0")]),t._v(".0+2\n")])])]),e("blockquote",[e("p",[t._v("该命令内部会去执行 "),e("code",[t._v("flutter build ios-framework --no-debug --no-profile ...")]),t._v(",并且使用的是 "),e("code",[t._v("Shorebird")]),t._v(" 魔改的 "),e("code",[t._v("Flutter")]),t._v(" 引擎!")])]),t._v(" "),e("p",[t._v("版本号可以在如下图所示进行查看")]),t._v(" "),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202401211436306.png",alt:""}})]),t._v(" "),e("p",[e("code",[t._v("ShoreBird")]),t._v(" 的内部逻辑会去以这个版本号组合,向服务器请求判断是否存在相应版本的相关补丁!")]),t._v(" "),e("p",[t._v("执行完成后,在 "),e("code",[t._v("Shorebird")]),t._v(" 控制台上可以看到相应的项")]),t._v(" "),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202401211437609.png",alt:""}})]),t._v(" "),e("p",[t._v("在命令执行前,请确保不存在 "),e("code",[t._v("7.0.0+2")]),t._v(" 的 "),e("code",[t._v("Release")]),t._v(",如果有的话,请先删除")]),t._v(" "),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202401211437151.png",alt:""}})]),t._v(" "),e("h2",{attrs:{id:"六、创建-shorebird-patch"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#六、创建-shorebird-patch"}},[t._v("#")]),t._v(" 六、创建 Shorebird Patch")]),t._v(" "),e("p",[t._v("紧急修复线上包的bug时操作,在 "),e("code",[t._v("Flutter")]),t._v(" 工程目录下执行")]),t._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[t._v("shorebird patch ios-framework-alpha --release-version "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("7.0")]),t._v(".0+2\n")])])]),e("p",[t._v("注:版本号与上述的 "),e("code",[t._v("release")]),t._v(" 命令中使用的要保持一致!")]),t._v(" "),e("p",[t._v("执行完成后,在 "),e("code",[t._v("Shorebird")]),t._v(" 控制台上点击对应的 "),e("code",[t._v("Release")]),t._v(" 项,进去后可以看到相应的补丁")]),t._v(" "),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202401211437542.png",alt:""}})]),t._v(" "),e("p",[t._v("看看这个补丁大小,我们再来看看安卓的补丁大小")]),t._v(" "),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202401211438158.png",alt:""}})]),t._v(" "),e("blockquote",[e("p",[t._v("一样的修改,安卓的补丁大小不到 "),e("code",[t._v("2 MB")]),t._v(","),e("code",[t._v("iOS")]),t._v(" 的补丁大小高达 "),e("code",[t._v("54.83 MB")]),t._v(" 😂")])]),t._v(" "),e("h2",{attrs:{id:"七、热更新验证"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#七、热更新验证"}},[t._v("#")]),t._v(" 七、热更新验证")]),t._v(" "),e("blockquote",[e("p",[t._v("官方文档上就只是说重启 "),e("code",[t._v("App")]),t._v(" 查看补丁是否生效,并没有说明失败了该如果排查问题~")])]),t._v(" "),e("p",[t._v("1、在执行完 "),e("code",[t._v("shorebird release")]),t._v(" 命令并完成上述原生工程的调整后,将原生工程的编译模式调整为 "),e("code",[t._v("Release")]),t._v(" 进行编译。")]),t._v(" "),e("p",[t._v("此时会依赖的 "),e("code",[t._v("flutter_modules/build/ios/framework/Release")]),t._v(" 下的 "),e("code",[t._v("xcframework")]),t._v(",备份为 "),e("code",[t._v("Release_release")])]),t._v(" "),e("p",[t._v("2、关闭 "),e("code",[t._v("App")]),t._v(",打 "),e("code",[t._v("patch")]),t._v(",注意,此时 "),e("code",[t._v("flutter_modules/build/ios/framework/Release")]),t._v(" 下的内容会被清空并重新创建。")]),t._v(" "),e("p",[t._v("3、打 "),e("code",[t._v("patch")]),t._v(" 后,将 "),e("code",[t._v("Release_release")]),t._v(" 改回 "),e("code",[t._v("Release")]),t._v(" 用 "),e("code",[t._v("Xcode")]),t._v(" 重新运行 "),e("code",[t._v("App")]),t._v(",一切正常的话即可看到变化。")]),t._v(" "),e("p",[t._v("无论成功还是失败,"),e("code",[t._v("Xcode")]),t._v(" 的控制台都会有相应的输出")]),t._v(" "),e("p",[t._v("成功")]),t._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 number"}},[t._v("2024")]),t._v("-01-03 "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("18")]),t._v(":37:55.838328+0800 xxx"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("623")]),t._v(":70498"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("VERBOSE0:shorebird.cc"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("151")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Shorebird updater: no active patch.\n"),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2024")]),t._v("-01-03 "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("18")]),t._v(":37:55.838424+0800 xxx"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("623")]),t._v(":70498"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("VERBOSE0:shorebird.cc"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("155")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Starting Shorebird update\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("00:00:00.002"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("1701cb000"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" INFO Sending patch check request: PatchCheckRequest "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" app_id: "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"')]),t._v(", channel: "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"stable"')]),t._v(", release_version: "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"7.0.0+2"')]),t._v(", patch_number: None, platform: "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"ios"')]),t._v(", arch: "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"aarch64"')]),t._v(" "),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("00:00:30.871"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("1701cb000"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" INFO Patch "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" successfully installed.\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("00:00:30.871"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("1701cb000"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" INFO Update result: Update installed\n")])])]),e("p",[t._v("失败")]),t._v(" "),e("blockquote",[e("p",[t._v("可以搜索关键字 "),e("code",[t._v("PatchCheckRequest")]),t._v(" 定位")])]),t._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 number"}},[t._v("2024")]),t._v("-01-03 "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("18")]),t._v(":37:55.838328+0800 xxx"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("623")]),t._v(":70498"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("VERBOSE0:shorebird.cc"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("151")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Shorebird updater: no active patch.\n"),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2024")]),t._v("-01-03 "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("18")]),t._v(":37:55.838424+0800 xxx"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("623")]),t._v(":70498"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("VERBOSE0:shorebird.cc"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("155")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" Starting Shorebird update\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("00:00:00.002"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("1701cb000"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" INFO Sending patch check request: PatchCheckRequest "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" app_id: "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"')]),t._v(", channel: "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"stable"')]),t._v(", release_version: "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"7.0.0+2"')]),t._v(", patch_number: None, platform: "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"ios"')]),t._v(", arch: "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"aarch64"')]),t._v(" "),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("00:00:30.871"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("1701cb000"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" ERROR Update failed: error decoding response body: operation timed out\nCaused by:\n operation timed out\n\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("00:00:30.871"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("1701cb000"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" INFO Update thread finished with status: Update had error\n")])])]),e("p",[t._v("该失败是因为国行机特有的网络权限导致的,开启 "),e("code",[t._v("Shorebird")]),t._v(" 的自动检查更新的话,会在网络权限被赋予前去请求,结果就是失败,所以需要关闭自动检查更新,使用 "),e("a",{attrs:{href:"https://pub.dev/packages/shorebird_code_push",target:"_blank",rel:"noopener noreferrer"}},[t._v("shorebird_code_push"),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("由于我们日常研发还是使用的是源码依赖的方式,只会在打最终测试包时才需要去做上述的调整操作,所以这里用我比较熟悉的 "),e("code",[t._v("Python")]),t._v(" 去制作了简易的脚本,并结合 "),e("code",[t._v("Jenkins")]),t._v(" 来辅助完成这种万年不变的无聊步骤")]),t._v(" "),e("p",[t._v("脚本已上传至 "),e("code",[t._v("Github")]),t._v(": "),e("a",{attrs:{href:"https://github.com/LinXunFeng/script_box/tree/main/flutter",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/LinXunFeng/script_box/tree/main/flutter"),e("OutboundLink")],1)]),t._v(" "),e("p",[t._v("看官可自取修改~")]),t._v(" "),e("h3",{attrs:{id:"switch-flutter-integrate-py"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#switch-flutter-integrate-py"}},[t._v("#")]),t._v(" switch_flutter_integrate.py")]),t._v(" "),e("blockquote",[e("p",[t._v("切换 "),e("code",[t._v("Flutter")]),t._v(" 项目的集成方式")])]),t._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 comment"}},[t._v("# 二进制依赖")]),t._v("\npython switch_flutter_integrate.py "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-p")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'原生工程路径'")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-m")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'binary'")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'ios'")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 源码依赖")]),t._v("\npython switch_flutter_integrate.py "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-p")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'原生工程路径'")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-m")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'source'")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'ios'")]),t._v(" \n")])])]),e("h3",{attrs:{id:"shorebird-py"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#shorebird-py"}},[t._v("#")]),t._v(" shorebird.py")]),t._v(" "),e("blockquote",[e("p",[t._v("自动获取版本号,并执行 "),e("code",[t._v("Shorebird")]),t._v(" 相关命令")])]),t._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 comment"}},[t._v("# release")]),t._v("\npython shorebird.py "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-p")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'原生工程路径'")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-s")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Flutter工程路径'")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-m")]),t._v(" release "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" ios\n\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# patch")]),t._v("\npython shorebird.py "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-p")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'原生工程路径'")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-s")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Flutter工程路径'")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-m")]),t._v(" patch "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" ios\n")])])]),e("p",[t._v("需要注意的是,"),e("code",[t._v("xcodeproj")]),t._v(" 和 "),e("code",[t._v("target")]),t._v(" 的名字被我固定写成 "),e("code",[t._v("OCProject")]),t._v(",如下代码中高亮的那两行,大家请先将其修改为自己的工程名再使用 "),e("code",[t._v("shorebird.py")]),t._v("。")]),t._v(" "),e("div",{staticClass:"language-diff extra-class"},[e("pre",{pre:!0,attrs:{class:"language-diff"}},[e("code",[t._v("def handle_ios():\n"),e("span",{pre:!0,attrs:{class:"token unchanged"}},[e("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(' """\n')]),e("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" 处理iOS项目\n")]),e("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(' """\n')]),e("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" # 1. 读取主版本号\n")]),e("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" # 请将 OCProject 修改为你们自己的工程名\n")])]),e("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[e("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" xcodeproj_path = os.path.join(project_path, 'OCProject.xcodeproj')\n")])]),e("span",{pre:!0,attrs:{class:"token unchanged"}},[e("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" version = ReleaseVersionTool.fetch_project_version(\n")]),e("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" xcodeproj_path=xcodeproj_path,\n")])]),e("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[e("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" target_name='OCProject',\n")])]),e("span",{pre:!0,attrs:{class:"token unchanged"}},[e("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" )\n")])])])])]),e("p",[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("code",[t._v("iOS")]),t._v(" 的热更新能用,但也仅仅只是能用,应用于很简单的应用程序,运行起来没有太明显的卡顿感知,但是稍微大点就可以感知到了,卡到怀疑人生那种,相比安卓端的没有任何性能损耗,iOS端的还需要再等等,毕竟现在 "),e("code",[t._v("iOS")]),t._v(" 还是 "),e("code",[t._v("Alpha")]),t._v(" 版本,相信不久将来 "),e("code",[t._v("Shorebird")]),t._v(" 团队会解决该问题。")]),t._v(" "),e("p",[t._v("具体关于安卓和 "),e("code",[t._v("iOS")]),t._v(" 两端之间的实现区别可以在这个 "),e("code",[t._v("issue")]),t._v(" 中查看 "),e("a",{attrs:{href:"https://github.com/shorebirdtech/shorebird/issues/871",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/shorebirdtech/shorebird/issues/871"),e("OutboundLink")],1)]),t._v(" "),e("p",[t._v("本篇到此结束,感谢大家的支持,我们下次再见! 👋")])])}),[],!1,null,null,null);e.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/139.62bcfa96.js b/assets/js/139.62bcfa96.js new file mode 100644 index 000000000..231c7ecce --- /dev/null +++ b/assets/js/139.62bcfa96.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[139],{451:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[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("Flutter")]),t._v(" 混编项目做了性能优化,其中最坑的,莫过于从 "),s("code",[t._v("Flutter")]),t._v(" 页面返回原生页面时 "),s("code",[t._v("dispose")]),t._v(" 方法竟然不走,如图所示")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202403101249921.gif",alt:""}})]),t._v(" "),s("p",[t._v("这就导致一些资源无法得到释放,如 "),s("code",[t._v("fijkplayer")]),t._v(" 所使用的视频资源。在原生页面与满屏视频的 "),s("code",[t._v("Flutter")]),t._v(" 页面之间反复进出时,内存占用越来越高")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202403101244604.png",alt:""}})]),t._v(" "),s("p",[t._v("上图是进出 "),s("code",[t._v("7")]),t._v(" 次视频页面后的内存占用情况。")]),t._v(" "),s("p",[t._v("最终会导致手机发烫,"),s("code",[t._v("App")]),t._v(" 变得卡顿,甚至最后闪退(当 "),s("code",[t._v("App")]),t._v(" 的内存占用达到图中红色虚线时就会直接被系统杀掉)")]),t._v(" "),s("p",[t._v("随即向 "),s("code",[t._v("Flutter")]),t._v(" 官方提出了 "),s("code",[t._v("issue")]),t._v(": "),s("a",{attrs:{href:"https://github.com/flutter/flutter/issues/137924",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/flutter/flutter/issues/137924"),s("OutboundLink")],1)]),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("issue")]),t._v(" 的第二天,组织成员 "),s("code",[t._v("dnfield")]),t._v(" 出提了一个 "),s("code",[t._v("PR")]),t._v(": "),s("a",{attrs:{href:"https://github.com/flutter/flutter/pull/137957",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/flutter/flutter/pull/137957"),s("OutboundLink")],1),t._v(" ,不过该 "),s("code",[t._v("PR")]),t._v(" 只是加了说明,解释为何会出现该问题,以及如何去处理。")]),t._v(" "),s("p",[t._v("根据其说明进行了优化,内存确实得到了有效的及时收回,如图所示")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202403101244527.png",alt:""}})]),t._v(" "),s("p",[t._v("该"),s("code",[t._v("PR")]),t._v("的说明指出:")]),t._v(" "),s("blockquote",[s("p",[t._v("当通过原生的方式关闭了 "),s("code",[t._v("Flutter")]),t._v(" 页面时,"),s("code",[t._v("Flutter")]),t._v(" 端并不知道开发者是否打算提前释放资源,所以 "),s("code",[t._v("Widget")]),t._v(" 树依旧保持着挂载的状态,这么做是为了再一次展示 "),s("code",[t._v("Flutter")]),t._v(" 页面时可以尽可能快的进行渲染。"),s("em",[t._v("【想必这也是上图中内存占用并没有得到彻底回落的原因!】")])]),t._v(" "),s("p",[t._v("如果想要尽快的释放资源,可以建立 "),s("code",[t._v("platform channel")]),t._v(",当返回原生页时告知 "),s("code",[t._v("Flutter")]),t._v(" 端去再次调用 "),s("code",[t._v("runApp")]),t._v(" 方法,并传入 "),s("code",[t._v("SizedBox.shrink")]),t._v(",这样就可以释放掉活跃的 "),s("code",[t._v("Widget")]),t._v(" 树。")])]),t._v(" "),s("p",[t._v("通过 "),s("code",[t._v("platform channel")]),t._v(" 的方式需要与原生端打交道,有没有纯 "),s("code",[t._v("Flutter")]),t._v(" 的方式呢?")]),t._v(" "),s("p",[t._v("有的,那就是通过监听 "),s("code",[t._v("Flutter App")]),t._v(" 的状态,当状态为 "),s("code",[t._v("AppLifecycleState.detached")]),t._v(" 时,就可以去执行 "),s("code",[t._v("runApp")]),t._v(" 方法了,达到一样释放资源的效果。")]),t._v(" "),s("p",[t._v("关于 "),s("code",[t._v("AppLifecycleState.detached")]),t._v(" 的说明:")]),t._v(" "),s("blockquote",[s("p",[t._v("此时 "),s("code",[t._v("Flutter App")]),t._v(" 仍被 "),s("code",[t._v("Flutter")]),t._v(" 引擎所持有,但已与任何的视图分离。也就是说引擎正在以没有视图的状态运行着。")]),t._v(" "),s("p",[t._v("该状态既可能是在 "),s("code",[t._v("Flutter")]),t._v(" 引擎初始化时附加视图的过程中,也可能是在视图因 "),s("code",[t._v("Navigator")]),t._v(" 弹出而被销毁之后。")])]),t._v(" "),s("p",[t._v("当 "),s("code",[t._v("App")]),t._v(" 从 "),s("code",[t._v("Flutter")]),t._v(" 页面返回原生页面后,就会切换到该状态。")]),t._v(" "),s("h2",{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("main")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("List")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" args"),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("runApp")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MyApp")]),s("span",{pre:!0,attrs:{class:"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\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("MyApp")]),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 keyword"}},[t._v("final")]),t._v(" bool isDetached"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MyApp")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("Key")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" 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("isDetached "),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("}")]),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 function"}},[t._v("@override")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("State")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MyApp")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),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("_MyAppState")]),s("span",{pre:!0,attrs:{class:"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(" _MyAppState "),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 generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MyApp")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WidgetsBindingObserver")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token metadata function"}},[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 class-name"}},[t._v("WidgetsBinding")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("instance"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("addObserver")]),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\n "),s("span",{pre:!0,attrs:{class:"token metadata function"}},[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 "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WidgetsBinding")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("instance"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("removeObserver")]),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 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\n "),s("span",{pre:!0,attrs:{class:"token metadata function"}},[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("didChangeAppLifecycleState")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AppLifecycleState")]),t._v(" state"),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("didChangeAppLifecycleState")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("state"),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("switch")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("state"),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("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AppLifecycleState")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("detached"),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 comment"}},[t._v("// https://github.com/flutter/flutter/pull/137957")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("runApp")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MyApp")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("isDetached"),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(")")]),s("span",{pre:!0,attrs:{class:"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 comment"}},[t._v("// https://github.com/Baseflow/flutter_cached_network_image/issues/429")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" imageCache "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PaintingBinding")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("instance"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("imageCache"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n imageCache"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("clear")]),s("span",{pre:!0,attrs:{class:"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 imageCache"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("clearLiveImages")]),s("span",{pre:!0,attrs:{class:"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("break")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("default")]),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("// This widget is the root of your application.")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@override")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),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 class-name"}},[t._v("BuildContext")]),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("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("isDetached"),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("// 引擎被detach, 移除所有的widget, 以此来及时释放资源")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),t._v(" resultWidget "),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 class-name"}},[t._v("SizedBox")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("shrink")]),s("span",{pre:!0,attrs:{class:"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("// Fix report no OKToast widget found.")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// resultWidget = OKToast(child: resultWidget);")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" resultWidget"),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MaterialApp")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("细心的朋友肯定注意到了 "),s("code",[t._v("OKToast")]),t._v(" 的相关代码,当时在 "),s("code",[t._v("Sentry")]),t._v(" 上看到了错误: "),s("code",[t._v("No OKToast widget found")]),t._v(",这是因为有些网络请求在返回原生页面后才请求成功,进而触发吐丝,而此时已经执行了 "),s("code",[t._v("runApp")]),t._v(" 去展示空白页面,考虑到即使取消了所有请求,有些业务会去对 "),s("code",[t._v("cancel")]),t._v(" 的情况去吐丝提醒,所以还是套一个 "),s("code",[t._v("OKToast")]),t._v(" 来得方便~")])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/14.9854ca03.js b/assets/js/14.9854ca03.js new file mode 100644 index 000000000..71cccb882 --- /dev/null +++ b/assets/js/14.9854ca03.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[14],{329:function(e,t,a){"use strict";a.r(t);var n=a(8),r=Object(n.a)({},(function(){var e=this,t=e._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[t("h2",{attrs:{id:"一、简述"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#一、简述"}},[e._v("#")]),e._v(" 一、简述")]),e._v(" "),t("p",[e._v("不管AbsListView(ListView、GridView)或是新出的RecyclerView,在使用notifyDataSetChanged方法更新列表数据时,一定要保证数据为同个对象(即hashCode要一致)。对于这个问题的论证,可以去看官方源代码,或是看我之前写的一篇博文"),t("a",{attrs:{href:"http://blog.csdn.net/csdn_lqr/article/details/50947825",target:"_blank",rel:"noopener noreferrer"}},[e._v('"解决ListViews适配器notifyDataSetChanged()无效问题"'),t("OutboundLink")],1),e._v(",相信可以帮到你。但是,这个不是本文的重点,本文重点讲解在"),t("strong",[e._v("Fragment")]),e._v('中,RecyclerView遇到notifyDataSetChanged无效的问题。如果你赶时间,可以直接看第三部分("总结")。')]),e._v(" "),t("h2",{attrs:{id:"二、探索"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#二、探索"}},[e._v("#")]),e._v(" 二、探索")]),e._v(" "),t("h3",{attrs:{id:"_1、查看数据-mdata-是否是同个对象"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1、查看数据-mdata-是否是同个对象"}},[e._v("#")]),e._v(" 1、查看数据(mData)是否是同个对象")]),e._v(" "),t("p",[e._v("*"),t("strong",[e._v("tip:java中可以通过打印hashCode的方式判断mData是否为同个对象。")])]),e._v(" "),t("p",[e._v("注意:initData方法在onActivityCreated()中被调用。")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",[t("code",[e._v('public void initData() {\n\tif (mData == null) {\n mData = new ArrayList<>();\n }\n\tmData.clear();\n ...\n\t数据填充\n\t...\n if (mAdapter == null) {\n mAdapter = new LQRAdapterForRecyclerView(getActivity(), mData, R.layout.item_senior) {\n @Override\n public void convert(LQRViewHolderForRecyclerView helper, String item, int position) {\n ...\n\t\t\t\t视图填充\n\t\t\t\t...\n }\n };\n mRvList.setAdapter(mAdapter);\n LogUtils.sf("setAdapter时mData地址:" + mData.hashCode());\n } else {\n mAdapter.notifyDataSetChanged();\n LogUtils.sf("setAdapter时mData地址:" + mData.hashCode());\n }\n}\n')])])]),t("h3",{attrs:{id:"_2、操作与结果"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2、操作与结果"}},[e._v("#")]),e._v(" 2、操作与结果")]),e._v(" "),t("p",[e._v("*"),t("strong",[e._v("tip:常规对Fragment的使用,会对其进行缓存,也可能使用单例模式,反正就是短时间内不会重新创建。")])]),e._v(" "),t("h4",{attrs:{id:"_1操作一"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1操作一"}},[e._v("#")]),e._v(" ①操作一:")]),e._v(" "),t("p",[e._v("打开Activity后,切换Fragment(第一次初始化Fragment)。显示效果如下:")]),e._v(" "),t("p",[t("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210107083249.png",alt:""}})]),e._v(" "),t("h4",{attrs:{id:"_2操作二"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2操作二"}},[e._v("#")]),e._v(" ②操作二:")]),e._v(" "),t("p",[e._v("切换别的Fragment后,再切回刚才的Fragment(此前该Fragment已经在存在,所以不会再次创建)。显示效果如下:")]),e._v(" "),t("p",[t("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210107083308.png",alt:""}})]),e._v(" "),t("h4",{attrs:{id:"_3看控制台"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3看控制台"}},[e._v("#")]),e._v(" ③看控制台:")]),e._v(" "),t("p",[t("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210107083320.png",alt:""}})]),e._v(" "),t("p",[e._v("可以看到数据对象地址一样,即为同一个。")]),e._v(" "),t("h3",{attrs:{id:"_3、查看recyclerview是否是同个对象"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_3、查看recyclerview是否是同个对象"}},[e._v("#")]),e._v(" 3、查看RecyclerView是否是同个对象")]),e._v(" "),t("p",[e._v("说实话,这个是踩坑经验丰富的网友在群里说的,如果不是他说出来,打死我也没想到,居然还有这么一个坑。从上面的结果可以看出,adapter中是有数据的没错,而且数据地址没变,所以理应notifyDataSetChanged()方法会生效。但是为什么会这样呢,这里先卖个关子,先看下面的操作。")]),e._v(" "),t("h4",{attrs:{id:"_1改下上面的代码-打印recyclerview的地址。"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1改下上面的代码-打印recyclerview的地址。"}},[e._v("#")]),e._v(" ①改下上面的代码,打印RecyclerView的地址。")]),e._v(" "),t("p",[e._v("代码如下:")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",[t("code",[e._v('public void initData() {\n\tif (mData == null) {\n mData = new ArrayList<>();\n }\n\tmData.clear();\n ...\n\t数据填充\n\t...\n if (mAdapter == null) {\n mAdapter = new LQRAdapterForRecyclerView(getActivity(), mData, R.layout.item_senior) {\n @Override\n public void convert(LQRViewHolderForRecyclerView helper, String item, int position) {\n ...\n\t\t\t\t视图填充\n\t\t\t\t...\n }\n };\n mRvList.setAdapter(mAdapter);\n LogUtils.sf("setAdapter时Rv:" + mRvList.hashCode());\n } else {\n mAdapter.notifyDataSetChanged();\n LogUtils.sf("notify时Rv:" + mRvList.hashCode());\n }\n}\n')])])]),t("h4",{attrs:{id:"_2同上述操作一致。"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2同上述操作一致。"}},[e._v("#")]),e._v(" ②同上述操作一致。")]),e._v(" "),t("p",[e._v("对同一个Fragment来回切换,看控制台输出。")]),e._v(" "),t("p",[t("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210107083334.png",alt:""}})]),e._v(" "),t("p",[e._v("果然不一样!!!")]),e._v(" "),t("h2",{attrs:{id:"三、总结"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#三、总结"}},[e._v("#")]),e._v(" 三、总结")]),e._v(" "),t("p",[e._v("为什么在Fragment中RecyclerView的地址会发生变化呢?我们先理清一下Fragment生命周期会陆续调用的几个方法:")]),e._v(" "),t("blockquote",[t("p",[e._v("onCreate() -> onCreateView() -> onActivityCreated() -> onDestroy()")])]),e._v(" "),t("p",[e._v("中间少了几个方法,请不用在意,下面贴下我的BaseFragment代码:")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",[t("code",[e._v("public abstract class BaseFragment extends Fragment {\n @Override\n public void onCreate(@Nullable Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n init();\n }\n\n @Nullable\n @Override\n public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n //子类不再需要设置布局ID,也不再需要使用ButterKnife.bind()\n View rootView = inflater.inflate(provideContentViewId(), container, false);\n ButterKnife.bind(this, rootView);\n initView(rootView);\n return rootView;\n }\n\n @Override\n public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n super.onActivityCreated(savedInstanceState);\n initData();\n initListener();\n }\n\n @Override\n public void onDestroy() {\n super.onDestroy();\n }\n}\n")])])]),t("p",[e._v("当一个Fragment在来回切换时,分别调用的方法如下:")]),e._v(" "),t("p",[e._v("第一次显示:")]),e._v(" "),t("blockquote",[t("p",[e._v("onCreate() -> onCreateView() -> onActivityCreated()")])]),e._v(" "),t("p",[e._v("第二次显示:")]),e._v(" "),t("blockquote",[t("p",[e._v("onCreateView() -> onActivityCreated()")])]),e._v(" "),t("p",[e._v("这里不难理解,因为Fragment一般使用的时候会被缓存,所以,当第二次显示的时候,不会调用onCreate()。只会调用onCreateView()和onActivityCreated(),这也就是RecyclerView地址不一样的原因所在,因为控件获取操作是在initView()中进行的,即RecyclerView的获取操作在onCreateView()中,而Fragment的每次显示都会调用onCreateView(),所以RecyclerView控件会被再次获取,即重新创建一个对象(此时hashCode就变化了)。")]),e._v(" "),t("h3",{attrs:{id:"_1、结论"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1、结论"}},[e._v("#")]),e._v(" 1、结论:")]),e._v(" "),t("p",[e._v("所以,在Fragment中使用RecyclerView或AbsListView控件的notifyDataSetChanged()方法时,除了保证数据(mData对象)不能变以外,控件本身一样也不能变。")]),e._v(" "),t("h3",{attrs:{id:"_2、解决方案"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2、解决方案"}},[e._v("#")]),e._v(" 2、解决方案:")]),e._v(" "),t("h4",{attrs:{id:"_1-方案一"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_1-方案一"}},[e._v("#")]),e._v(" 1)方案一:")]),e._v(" "),t("p",[e._v("因为Fragment的onCreateView()和onActivityCreated()方法在每次Fragment显示的时候会被调用,控件会被重新创建一次,所以,解决方法只能是在这两个方法中重新对RecyclerView设置适配器,而不要使用notifyDataSetChanged(),故代码改为如下:")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",[t("code",[e._v("public void initData() {\n if (mData == null) {\n mData = new ArrayList<>();\n }\n mData.clear();\n ...\n 数据填充\n ...\n if (mAdapter == null) {\n mAdapter = new LQRAdapterForRecyclerView(getActivity(), mData, R.layout.item_senior) {\n @Override\n public void convert(LQRViewHolderForRecyclerView helper, String item, int position) {\n ...\n 视图填充\n ...\n }\n };\n } \n mRvList.setAdapter(mAdapter);\n}\n")])])]),t("p",[e._v("注:只是建议不要在上述两个生命周期方法中使用notifyDataSetChanged()而已,只要在保证RecyclerView等列表控件设置完适配器后,可以在任意地方继续使用notifyDataSetChanged()。")]),e._v(" "),t("h4",{attrs:{id:"_2-方案二"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#_2-方案二"}},[e._v("#")]),e._v(" 2)方案二:")]),e._v(" "),t("p",[e._v("让rootView作为全局变量,在回调onCreateView()时不再重新创建。")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",[t("code",[e._v("public abstract class BaseFragment extends Fragment {\n\n View rootView;\n\n @Override\n public void onCreate(@Nullable Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n init();\n }\n\n @Nullable\n @Override\n public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n //子类不再需要设置布局ID,也不再需要使用ButterKnife.bind()\n if(rootView == null){\n rootView = inflater.inflate(provideContentViewId(), container, false);\n ButterKnife.bind(this, rootView);\n initView(rootView);\n }\n return rootView;\n }\n\n @Override\n public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n super.onActivityCreated(savedInstanceState);\n initData();\n initListener();\n }\n\n @Override\n public void onDestroy() {\n super.onDestroy();\n }\n}\n")])])])])}),[],!1,null,null,null);t.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/140.add30aaf.js b/assets/js/140.add30aaf.js new file mode 100644 index 000000000..f7ff240f7 --- /dev/null +++ b/assets/js/140.add30aaf.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[140],{452:function(e,t,s){"use strict";s.r(t);var a=s(8),n=Object(a.a)({},(function(){var e=this,t=e._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[t("iframe",{attrs:{src:"//player.bilibili.com/player.html?aid=1603068977&bvid=BV1Qm421L7ib&cid=1505310266&p=1",scrolling:"no",border:"0",frameborder:"no",framespacing:"0",allowfullscreen:"true",width:"100%",height:"560px"}}),e._v(" "),t("h2",{attrs:{id:"一、背景"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#一、背景"}},[e._v("#")]),e._v(" 一、背景")]),e._v(" "),t("p",[e._v("上周一尝试从 "),t("code",[e._v("3.16.9")]),e._v(" 升级 "),t("code",[e._v("3.19.3")]),e._v(",主要有两个原因:")]),e._v(" "),t("p",[e._v("一是安卓端有一个疑似造成崩溃率上涨的 "),t("code",[e._v("bug")]),e._v(" 在 "),t("code",[e._v("Flutter 3.16")]),e._v(" 上出现,相关issue: "),t("a",{attrs:{href:"https://github.com/flutter/flutter/issues/138947",target:"_blank",rel:"noopener noreferrer"}},[e._v("#138947"),t("OutboundLink")],1),e._v(", 该 "),t("code",[e._v("bug")]),e._v(" 在 "),t("code",[e._v("3.13")]),e._v(" 不会出现,在 "),t("code",[e._v("3.17.pre")]),e._v(" 上得到修复,而 "),t("code",[e._v("3.16")]),e._v(" 之后的下个正式版本是 "),t("code",[e._v("3.19")]),e._v("。")]),e._v(" "),t("p",[e._v("二是苹果的隐私清单审核政策。")]),e._v(" "),t("p",[e._v("在苹果发布的【"),t("a",{attrs:{href:"https://developer.apple.com/cn/news/?id=3d8a9yyh",target:"_blank",rel:"noopener noreferrer"}},[e._v("关于 App Store 提交的隐私更新"),t("OutboundLink")],1),e._v("】新闻中指出")]),e._v(" "),t("blockquote",[t("p",[t("strong",[e._v("自 3 月 13 日起:")]),e._v(" 如果你上传新 App 或更新 App 到 App Store Connect,且该 App 使用了需要声明批准原因的 API,但你未在 App 的隐私清单中提供批准原因,我们会通过电子邮件告知你。这是对 App Store Connect 中现有通知的补充。")]),e._v(" "),t("p",[t("strong",[e._v("自 5 月 1 日起:")]),e._v(" 你需要就你的 App 代码使用的所列 API 提供批准原因,才能将新 App 或更新 App 上传到 App Store Connect。如果你没有合理的原因使用某个 API,请寻找替代的方案。如果你添加了常用第三方 SDK 列表中所列的新版第三方 SDK,那么这些 API、隐私清单和签名要求将应用于该 SDK。请务必使用包含其隐私清单的 SDK 版本,并注意在将该 SDK 添加为二进制依赖项时也需要提供签名。")])]),e._v(" "),t("p",[e._v("在苹果的【"),t("a",{attrs:{href:"https://developer.apple.com/cn/support/third-party-SDK-requirements/",target:"_blank",rel:"noopener noreferrer"}},[e._v("即将发布的第三方SDK要求"),t("OutboundLink")],1),e._v("】一文中,列举出需要隐私清单和签名的 "),t("code",[e._v("SDK")]),e._v(",其中就包含了 "),t("code",[e._v("Flutter")]),e._v("。为了符合该审核要求,"),t("code",[e._v("Flutter")]),e._v(" 从 "),t("code",[e._v("3.19")]),e._v(" 开始包含了 "),t("code",[e._v("PrivacyInfo.xcprivacy")]),e._v(" 这个隐私清单文件。")]),e._v(" "),t("p",[e._v("文件位于: "),t("a",{attrs:{href:"https://github.com/flutter/engine/blob/3.19.0/shell/platform/darwin/ios/framework/PrivacyInfo.xcprivacy",target:"_blank",rel:"noopener noreferrer"}},[e._v("https://github.com/flutter/engine/blob/3.19.0/shell/platform/darwin/ios/framework/PrivacyInfo.xcprivacy"),t("OutboundLink")],1)]),e._v(" "),t("h2",{attrs:{id:"二、踩坑"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#二、踩坑"}},[e._v("#")]),e._v(" 二、踩坑")]),e._v(" "),t("p",[e._v("升到到 "),t("code",[e._v("3.19.3")]),e._v(" 后发现,从 "),t("code",[e._v("页面A")]),e._v(" 跳转到 "),t("code",[e._v("页面B")]),e._v(" 和返回 "),t("code",[e._v("页面A")]),e._v(" 时,"),t("code",[e._v("页面A")]),e._v(" 的 "),t("code",[e._v("build")]),e._v(" 方法都会被执行,降回 "),t("code",[e._v("3.16.9")]),e._v(" 则不会,这就很奇怪。后来发现是因为 "),t("code",[e._v("页面A")]),e._v(" 间接使用了 "),t("code",[e._v("ModalRoute.of")]),e._v("。")]),e._v(" "),t("p",[e._v("以下是可复现问题的代码")]),e._v(" "),t("div",{staticClass:"language-diff extra-class"},[t("pre",{pre:!0,attrs:{class:"language-diff"}},[t("code",[e._v("class PageA extends StatefulWidget {\n"),t("span",{pre:!0,attrs:{class:"token unchanged"}},[t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" @override\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" State createState() => _PageAState();\n")])]),e._v("}\n\nclass _PageAState extends State {\n"),t("span",{pre:!0,attrs:{class:"token unchanged"}},[t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" @override\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" Widget build(BuildContext context) {\n")])]),e._v("\n"),t("span",{pre:!0,attrs:{class:"token unchanged"}},[t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" // ==== 这里 ====\n")])]),t("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" final arguments = ModalRoute.of(context)?.settings.arguments;\n")]),t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(' print("PageA arguments:$arguments");\n')])]),e._v("\n"),t("span",{pre:!0,attrs:{class:"token unchanged"}},[t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" return Scaffold(\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" appBar: AppBar(\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" backgroundColor: Theme.of(context).colorScheme.inversePrimary,\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" title: const Text('PageA'),\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" ),\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" body: const SizedBox.shrink(),\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" );\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" }\n")])]),e._v("}\n")])])]),t("h2",{attrs:{id:"三、探索"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#三、探索"}},[e._v("#")]),e._v(" 三、探索")]),e._v(" "),t("p",[e._v("在经过一番摸索后,发现 "),t("code",[e._v("ModalRoute")]),e._v(" 在 "),t("code",[e._v("3.19")]),e._v(" 上面有一个小修改~")]),e._v(" "),t("p",[e._v("相关 "),t("code",[e._v("issue")]),e._v(" 是: "),t("a",{attrs:{href:"https://github.com/flutter/flutter/issues/112567",target:"_blank",rel:"noopener noreferrer"}},[e._v("#112567"),t("OutboundLink")],1),e._v(" 。")]),e._v(" "),t("p",[e._v("该 "),t("code",[e._v("issue")]),e._v(" 主要是涉及在 "),t("code",[e._v("Web")]),e._v(" 端上按 "),t("code",[e._v("Tab")]),e._v(" 键切换焦点的问题,后续有个 "),t("code",[e._v("PR")]),e._v(": "),t("a",{attrs:{href:"https://github.com/flutter/flutter/pull/130841",target:"_blank",rel:"noopener noreferrer"}},[e._v("#130841"),t("OutboundLink")],1),e._v(" 解决了该问题。")]),e._v(" "),t("blockquote",[t("p",[e._v("该 "),t("code",[e._v("PR")]),e._v(" 因内部测试原因进行了回滚,后再重新登陆,现PR: "),t("a",{attrs:{href:"https://github.com/flutter/flutter/pull/134554",target:"_blank",rel:"noopener noreferrer"}},[e._v("#134554"),t("OutboundLink")],1)])]),e._v(" "),t("p",[e._v("而在该 "),t("code",[e._v("PR")]),e._v(" 中就对 "),t("code",[e._v("ModalRoute")]),e._v(" 加了如下代码:")]),e._v(" "),t("div",{staticClass:"language-diff extra-class"},[t("pre",{pre:!0,attrs:{class:"language-diff"}},[t("code",[e._v("// packages/flutter/lib/src/widgets/routes.dart\n\nabstract class ModalRoute extends TransitionRoute with LocalHistoryRoute {\n"),t("span",{pre:!0,attrs:{class:"token unchanged"}},[t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" ...\n")])]),t("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" @override\n")]),t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" void didChangeNext(Route? nextRoute) {\n")]),t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" super.didChangeNext(nextRoute);\n")]),t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" changedInternalState();\n")]),t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" }\n")]),t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" @override\n")]),t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" void didPopNext(Route nextRoute) {\n")]),t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" super.didPopNext(nextRoute);\n")]),t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" changedInternalState();\n")]),t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" }\n")]),t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")])]),t("span",{pre:!0,attrs:{class:"token unchanged"}},[t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" @override\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" void changedInternalState() {\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" super.changedInternalState();\n")])]),t("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[t("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" setState(() { /* internal state already changed */ });\n")]),t("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" _modalBarrier.markNeedsBuild();\n")])]),t("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" // No need to mark dirty if this method is called during build phase.\n")]),t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" if (SchedulerBinding.instance.schedulerPhase != SchedulerPhase.persistentCallbacks) {\n")]),t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" setState(() { /* internal state already changed */ });\n")]),t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" _modalBarrier.markNeedsBuild();\n")]),t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" }\n")])]),t("span",{pre:!0,attrs:{class:"token unchanged"}},[t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" _modalScope.maintainState = maintainState;\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" }\n")])]),e._v("...\n}\n")])])]),t("p",[t("code",[e._v("didChangeNext")]),e._v(" 和 "),t("code",[e._v("didPopNext")]),e._v(" 这两个方法对应的就是页面的 "),t("code",[e._v("push")]),e._v(" 和 "),t("code",[e._v("pop")]),e._v(",现在在该 "),t("code",[e._v("PR")]),e._v(" 中重写并调用了 "),t("code",[e._v("changedInternalState")]),e._v(" 方法,在 "),t("code",[e._v("changedInternalState")]),e._v(" 方法中调用了 "),t("code",[e._v("setState")]),e._v("。")]),e._v(" "),t("p",[e._v("下面将以高亮的方式标出重点代码(不是新增代码)。")]),e._v(" "),t("div",{staticClass:"language-diff extra-class"},[t("pre",{pre:!0,attrs:{class:"language-diff"}},[t("code",[e._v("abstract class ModalRoute extends TransitionRoute with LocalHistoryRoute {\n"),t("span",{pre:!0,attrs:{class:"token unchanged"}},[t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" ...\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" @protected\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" void setState(VoidCallback fn) {\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" if (_scopeKey.currentState != null) {\n")])]),t("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" _scopeKey.currentState!._routeSetState(fn);\n")])]),t("span",{pre:!0,attrs:{class:"token unchanged"}},[t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" } else {\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" // The route isn't currently visible, so we don't have to call its setState\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" // method, but we do still need to call the fn callback, otherwise the state\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" // in the route won't be updated!\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" fn();\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" }\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" }\n")])]),e._v("...\n}\n")])])]),t("p",[e._v("这个 "),t("code",[e._v("ModalRoute")]),e._v(" 内的 "),t("code",[e._v("setState")]),e._v(" 会使 "),t("code",[e._v("_ModalScopeStatus")]),e._v(" 的 "),t("code",[e._v("_routeSetState")]),e._v(" 被调用,然后触发 "),t("code",[e._v("_ModalScopeState")]),e._v(" 的 "),t("code",[e._v("setState")]),e._v(",接着其 "),t("code",[e._v("child: _ModalScopeStatus")]),e._v(" 就开始 "),t("code",[e._v("rebuild")]),e._v(" 了。")]),e._v(" "),t("div",{staticClass:"language-diff extra-class"},[t("pre",{pre:!0,attrs:{class:"language-diff"}},[t("code",[e._v("class _ModalScopeState extends State<_ModalScope> {\n"),t("span",{pre:!0,attrs:{class:"token unchanged"}},[t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" ...\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" void _routeSetState(VoidCallback fn) {\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" if (widget.route.isCurrent && !_shouldIgnoreFocusRequest && _shouldRequestFocus) {\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" widget.route.navigator!.focusNode.enclosingScope?.setFirstFocus(focusScopeNode);\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" }\n")])]),t("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" setState(fn);\n")])]),t("span",{pre:!0,attrs:{class:"token unchanged"}},[t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" }\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" \n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" \n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" @override\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" Widget build(BuildContext context) {\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" return AnimatedBuilder(\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" ...\n")])]),t("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" child: _ModalScopeStatus(\n")])]),t("span",{pre:!0,attrs:{class:"token unchanged"}},[t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" ...\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" ),\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" );\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" }\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" ...\n")])]),e._v("}\n")])])]),t("p",[e._v("如下代码所示,"),t("code",[e._v("_ModalScopeStatus")]),e._v(" 是一个 "),t("code",[e._v("InheritedWidget")]),e._v(",在经过一系列的处理后最终会走到其 "),t("code",[e._v("InheritedElement")]),e._v(" 的 "),t("code",[e._v("update")]),e._v(" 方法,在 "),t("code",[e._v("update")]),e._v(" 方法中通过调用 "),t("code",[e._v("updateShouldNotify")]),e._v(" 来判断数据是否发生变化,进而决定是否通知相关依赖。")]),e._v(" "),t("div",{staticClass:"language-diff extra-class"},[t("pre",{pre:!0,attrs:{class:"language-diff"}},[t("code",[t("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" class _ModalScopeStatus extends InheritedWidget {\n")])]),t("span",{pre:!0,attrs:{class:"token unchanged"}},[t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" ...\n")])]),e._v("\n"),t("span",{pre:!0,attrs:{class:"token unchanged"}},[t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" @override\n")])]),t("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" bool updateShouldNotify(_ModalScopeStatus old) {\n")]),t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" return isCurrent != old.isCurrent ||\n")])]),t("span",{pre:!0,attrs:{class:"token unchanged"}},[t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" canPop != old.canPop ||\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" impliesAppBarDismissal != old.impliesAppBarDismissal ||\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" route != old.route;\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" }\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" ...\n")])]),e._v("}\n")])])]),t("div",{staticClass:"language-diff extra-class"},[t("pre",{pre:!0,attrs:{class:"language-diff"}},[t("code",[e._v("class InheritedElement extends ProxyElement {\n"),t("span",{pre:!0,attrs:{class:"token unchanged"}},[t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" ...\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" @override\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" void updated(InheritedWidget oldWidget) {\n")])]),t("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" if ((widget as InheritedWidget).updateShouldNotify(oldWidget)) {\n")])]),t("span",{pre:!0,attrs:{class:"token unchanged"}},[t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" super.updated(oldWidget);\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" }\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" }\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" ...\n")])]),e._v("}\n")])])]),t("p",[t("code",[e._v("_ModalScopeStatus")]),e._v(" 的 "),t("code",[e._v("isCurrent")]),e._v(" 表示当前页面是否处于最上层,所以在打开和关闭下一个页面时,其值必定切换,也就是 "),t("code",[e._v("updateShouldNotify")]),e._v(" 必定返回 "),t("code",[e._v("true")]),e._v(",既而通知依赖(实际上就是找出一个个依赖进行标脏,然后等待 "),t("code",[e._v("build")]),e._v(" 方法的重新调用)。")]),e._v(" "),t("p",[e._v("而我们在使用 "),t("code",[e._v("ModalRoute.of")]),e._v(" 的时候,内部就是将当前页的 "),t("code",[e._v("BuildContext")]),e._v(" 添加到依赖中,所以他这个改动就会影响到使用 "),t("code",[e._v("ModalRoute.of")]),e._v(" 的 "),t("code",[e._v("Widget")]),e._v(",使其多次 "),t("code",[e._v("rebuild")]),e._v("。")]),e._v(" "),t("div",{staticClass:"language-dart extra-class"},[t("pre",{pre:!0,attrs:{class:"language-dart"}},[t("code",[t("span",{pre:!0,attrs:{class:"token metadata function"}},[e._v("@optionalTypeArgs")]),e._v("\n"),t("span",{pre:!0,attrs:{class:"token keyword"}},[e._v("static")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[e._v("ModalRoute")]),t("span",{pre:!0,attrs:{class:"token generics"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[e._v("T")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(">")])]),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("?")]),e._v(" of"),t("span",{pre:!0,attrs:{class:"token generics"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[e._v("T")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[e._v("extends")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[e._v("Object")]),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("?")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(">")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[e._v("BuildContext")]),e._v(" context"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("{")]),e._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[e._v("final")]),e._v(" _ModalScopeStatus"),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("?")]),e._v(" widget "),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),e._v(" context"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("dependOnInheritedWidgetOfExactType"),t("span",{pre:!0,attrs:{class:"token generics"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("<")]),e._v("_ModalScopeStatus"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(">")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(";")]),e._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[e._v("return")]),e._v(" widget"),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("?")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("route "),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("as")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[e._v("ModalRoute")]),t("span",{pre:!0,attrs:{class:"token generics"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[e._v("T")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(">")])]),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("?")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(";")]),e._v("\n"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("}")]),e._v("\n")])])]),t("h2",{attrs:{id:"四、解决方案"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#四、解决方案"}},[e._v("#")]),e._v(" 四、解决方案")]),e._v(" "),t("h3",{attrs:{id:"方案一-调整-modalroute-of"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#方案一-调整-modalroute-of"}},[e._v("#")]),e._v(" 方案一:调整 "),t("code",[e._v("ModalRoute.of")])]),e._v(" "),t("p",[e._v("在当前版本中,"),t("code",[e._v("of")]),e._v(" 的用意就是找到相应的 "),t("code",[e._v("ModalRoute")]),e._v(" 并且创建依赖关系,当数据改变时会重新 "),t("code",[e._v("build")]),e._v(" ,这是符合它期望用意的。")]),e._v(" "),t("p",[e._v("但是有些场景下我们并不希望有这个 “"),t("strong",[e._v("特性")]),e._v("”,比如,我打开新页面后,通过 "),t("code",[e._v("ModalRoute.of(context)?.settings.arguments")]),e._v(" 取路由参数,当前页面的取参,与跳转和关闭下个页面是没有任何关系的,所以这种场景下触发 "),t("code",[e._v("rebuild")]),e._v(" 将毫无意义。")]),e._v(" "),t("p",[e._v("所以我提了个 "),t("code",[e._v("PR")]),e._v(": "),t("a",{attrs:{href:"https://github.com/flutter/flutter/pull/145389",target:"_blank",rel:"noopener noreferrer"}},[e._v("#145389"),t("OutboundLink")],1),e._v(", 给 "),t("code",[e._v("ModalRoute.of")]),e._v(" 添加了 "),t("code",[e._v("createDependency")]),e._v(" 参数,为开发者提供了是否创建依赖的选择。目前还在审核中~")]),e._v(" "),t("div",{staticClass:"language-dart extra-class"},[t("pre",{pre:!0,attrs:{class:"language-dart"}},[t("code",[e._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[e._v("static")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[e._v("ModalRoute")]),t("span",{pre:!0,attrs:{class:"token generics"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[e._v("T")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(">")])]),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("?")]),e._v(" of"),t("span",{pre:!0,attrs:{class:"token generics"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[e._v("T")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[e._v("extends")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[e._v("Object")]),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("?")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(">")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),e._v("\n "),t("span",{pre:!0,attrs:{class:"token class-name"}},[e._v("BuildContext")]),e._v(" context"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(",")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("{")]),e._v("\n bool createDependency "),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token boolean"}},[e._v("true")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(",")]),e._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("}")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("{")]),e._v("\n _ModalScopeStatus"),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("?")]),e._v(" widget"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(";")]),e._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[e._v("if")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),e._v("createDependency"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("{")]),e._v("\n widget "),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),e._v(" context"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("dependOnInheritedWidgetOfExactType"),t("span",{pre:!0,attrs:{class:"token generics"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("<")]),e._v("_ModalScopeStatus"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(">")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(";")]),e._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("}")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token keyword"}},[e._v("else")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("{")]),e._v("\n widget "),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),e._v(" context\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("getElementForInheritedWidgetOfExactType"),t("span",{pre:!0,attrs:{class:"token generics"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("<")]),e._v("_ModalScopeStatus"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(">")])]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\n "),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("?")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("widget "),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("as")]),e._v(" _ModalScopeStatus"),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("?")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(";")]),e._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("}")]),e._v("\n "),t("span",{pre:!0,attrs:{class:"token keyword"}},[e._v("return")]),e._v(" widget"),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("?")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("route "),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("as")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token class-name"}},[e._v("ModalRoute")]),t("span",{pre:!0,attrs:{class:"token generics"}},[t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("<")]),t("span",{pre:!0,attrs:{class:"token class-name"}},[e._v("T")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(">")])]),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("?")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(";")]),e._v("\n "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("}")]),e._v("\n")])])]),t("h3",{attrs:{id:"方案二-魔改源码"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#方案二-魔改源码"}},[e._v("#")]),e._v(" 方案二:魔改源码")]),e._v(" "),t("p",[e._v("原 "),t("code",[e._v("PR")]),e._v(" 是对 "),t("code",[e._v("Tab")]),e._v(" 键切换焦点问题的修复,但对于移动端来说根本不算问题,因为用不上~ 😅 (当然,如果你们的用户有使用无障碍功能的,还需要自行斟酌一下)")]),e._v(" "),t("p",[e._v("如果这个问题到时还未解决(原 "),t("code",[e._v("PR")]),e._v(" 的作者还在休假),那我们也可以先注释掉相关代码对 "),t("code",[e._v("changedInternalState")]),e._v(" 的调用来应对")]),e._v(" "),t("div",{staticClass:"language-diff extra-class"},[t("pre",{pre:!0,attrs:{class:"language-diff"}},[t("code",[e._v("// packages/flutter/lib/src/widgets/routes.dart\n\nabstract class ModalRoute extends TransitionRoute with LocalHistoryRoute {\n"),t("span",{pre:!0,attrs:{class:"token unchanged"}},[t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" ...\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" @override\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" void didChangeNext(Route? nextRoute) {\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" super.didChangeNext(nextRoute);\n")])]),t("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" // changedInternalState();\n")])]),t("span",{pre:!0,attrs:{class:"token unchanged"}},[t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" }\n")])]),e._v("\n"),t("span",{pre:!0,attrs:{class:"token unchanged"}},[t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" @override\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" void didPopNext(Route nextRoute) {\n")]),t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" super.didPopNext(nextRoute);\n")])]),t("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[t("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" // changedInternalState();\n")])]),t("span",{pre:!0,attrs:{class:"token unchanged"}},[t("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),t("span",{pre:!0,attrs:{class:"token line"}},[e._v(" }\n")])]),e._v("}\n")])])]),t("p",[e._v("提供一个补丁")]),e._v(" "),t("div",{staticClass:"language-shell extra-class"},[t("pre",{pre:!0,attrs:{class:"language-shell"}},[t("code",[t("span",{pre:!0,attrs:{class:"token comment"}},[e._v("# 进入你的 flutter 目录,比如我用的是 fvm 下载的 3.19.3")]),e._v("\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[e._v("# 记得将 cd 后面的路径换成你自己电脑上的~")]),e._v("\n"),t("span",{pre:!0,attrs:{class:"token builtin class-name"}},[e._v("cd")]),e._v(" /Users/lxf/fvm/versions/3.19.3\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[e._v("# 下载补丁")]),e._v("\n"),t("span",{pre:!0,attrs:{class:"token function"}},[e._v("curl")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token parameter variable"}},[e._v("-O")]),e._v(" https://raw.githubusercontent.com/LinXunFeng/flutter_assets/main/patch/01_rollbak_3_19_routes_change/0001-Roll-back-changes-to-routes.dart.patch\n\n"),t("span",{pre:!0,attrs:{class:"token comment"}},[e._v("# 应用补丁")]),e._v("\n"),t("span",{pre:!0,attrs:{class:"token function"}},[e._v("git")]),e._v(" apply 0001-Roll-back-changes-to-routes.dart.patch\n")])])]),t("h2",{attrs:{id:"五、最后"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#五、最后"}},[e._v("#")]),e._v(" 五、最后")]),e._v(" "),t("p",[e._v("总而言之,距离5月1日(苹果强制要求添加隐私清单文件的期限)还有一个月,我们现在大可保持在 "),t("code",[e._v("3.13")]),e._v(" 版本先用着,免得折腾,同时也祈祷快点修复该问题,然后顺利升级上去~")])])}),[],!1,null,null,null);t.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/141.54f29e7a.js b/assets/js/141.54f29e7a.js new file mode 100644 index 000000000..004d9aee6 --- /dev/null +++ b/assets/js/141.54f29e7a.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[141],{453:function(t,e,a){"use strict";a.r(e);var s=a(8),r=Object(s.a)({},(function(){var t=this,e=t._self._c;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("等了这么久,就在 "),e("code",[t._v("4月9号")]),t._v(" 这一天,"),e("code",[t._v("Shorebird")]),t._v(" 的 "),e("code",[t._v("1.0")]),t._v(" 正式版终于来了!本次最关心的还是对 "),e("code",[t._v("iOS")]),t._v(" 的热更新支持得如何了,因为在之前的版本中,"),e("code",[t._v("iOS")]),t._v(" 的性能表现简直差到无法使用~")]),t._v(" "),e("p",[t._v("先给个结论:"),e("code",[t._v("iOS")]),t._v(" 端终于能用了!通过测试,"),e("code",[t._v("iOS")]),t._v(" 端打完补丁后的性能表现已达上线水平 🥳")]),t._v(" "),e("p",[t._v("需要注意的是:")]),t._v(" "),e("blockquote",[e("p",[e("code",[t._v("iOS")]),t._v(" 热更新稳定版本需要使用最新版本的 "),e("code",[t._v("Shorebird CLI (1.0.0)")]),t._v(" 和最新稳定版本的 "),e("code",[t._v("Flutter (3.19.5)")]),t._v("。")])]),t._v(" "),e("p",[e("strong",[t._v("但是")])]),t._v(" "),e("p",[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("code",[t._v("1.0")]),t._v(" 来尝尝鲜,结果在打完补丁后,有几个页面灰屏了!!!")]),t._v(" "),e("p",[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("bool "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("_isExist")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SomeType")]),t._v(" type"),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("try")]),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(" model "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" state"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("models"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("firstWhere")]),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("element"),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(" element"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("type "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" type"),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 keyword"}},[t._v("return")]),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 punctuation"}},[t._v("}")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("catch")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e"),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 boolean"}},[t._v("false")]),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("div",{staticClass:"language-dart extra-class"},[e("pre",{pre:!0,attrs:{class:"language-dart"}},[e("code",[e("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" value "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string-literal"}},[e("span",{pre:!0,attrs:{class:"token string"}},[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 keyword"}},[t._v("try")]),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(" json"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("decode")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value"),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(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("catch")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e"),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("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("tryCatch")]),t._v(" 失效了,然后我建了一个最小的 "),e("code",[t._v("Demo")]),t._v(" 做测试,竟然一切正常。。。")]),t._v(" "),e("p",[t._v("然后开始忙需求去了,直到 "),e("code",[t._v("4月15号")]),t._v(" 小伙伴说官方有提到相关说明,如下图所示")]),t._v(" "),e("p",[e("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202404172156171.png",alt:""}})]),t._v(" "),e("p",[t._v("还真是 "),e("code",[t._v("tryCatch")]),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("关于基本使用,这里就不再赘述,有需要的小伙伴可以看我之前的两篇文章")]),t._v(" "),e("ul",[e("li",[e("RouterLink",{attrs:{to:"/pages/a0e176/"}},[t._v("Flutter - 混编项目集成Shorebird热更新🐦(安卓篇)")])],1),t._v(" "),e("li",[e("RouterLink",{attrs:{to:"/pages/e6f2c4/"}},[t._v("Flutter - 混编项目集成Shorebird热更新🐦(iOS篇)")])],1)]),t._v(" "),e("p",[t._v("我们接下来讲一讲升级到正式版后的一些调整")]),t._v(" "),e("blockquote",[e("p",[t._v("以下是 "),e("code",[t._v("Shorebird")]),t._v(" 的 "),e("code",[t._v("1.0")]),t._v(" 与 "),e("code",[t._v("0.22.1")]),t._v(" 版本之间的命令改动")])]),t._v(" "),e("h3",{attrs:{id:"ios-framework-alpha-改名为-ios-framework"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#ios-framework-alpha-改名为-ios-framework"}},[t._v("#")]),t._v(" "),e("code",[t._v("ios-framework-alpha")]),t._v(" 改名为 "),e("code",[t._v("ios-framework")])]),t._v(" "),e("div",{staticClass:"language-diff extra-class"},[e("pre",{pre:!0,attrs:{class:"language-diff"}},[e("code",[e("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[e("span",{pre:!0,attrs:{class:"token prefix deleted"}},[t._v("-")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" shorebird release ios-framework-alpha --release-version 7.0.0+1\n")])]),e("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[e("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" shorebird release ios-framework --release-version 7.0.0+1\n")])])])])]),e("h3",{attrs:{id:"flutter-versions-use-废弃"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#flutter-versions-use-废弃"}},[t._v("#")]),t._v(" "),e("code",[t._v("flutter versions use")]),t._v(" 废弃")]),t._v(" "),e("p",[t._v("现在你再按如下命令执行,会提示你找不到 "),e("code",[t._v("use")])]),t._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[t._v("shorebird flutter versions use "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("3.19")]),t._v(".3\nCould not "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("find")]),t._v(" a subcommand named "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"use"')]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"shorebird flutter versions"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v("\n\nUsage: shorebird flutter versions "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("subcommand"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("arguments"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n-h, "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--help")]),t._v(" Print this usage information.\n\nAvailable subcommands:\n list List available Flutter versions.\n\nRun "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"shorebird help"')]),t._v(" to see global options.\n")])])]),e("p",[t._v("如果你需要指定 "),e("code",[t._v("Flutter")]),t._v(" 版本,就得在 "),e("code",[t._v("release")]),t._v(" 命令中使用 "),e("code",[t._v("--flutter-version")])]),t._v(" "),e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[t._v("# 安卓\nshorebird release aar --release-version=7.0.0+1 --flutter-version=3.19.5\n\n# iOS\nshorebird release ios-framework --release-version=7.0.0+1 --flutter-version=3.19.5\n")])])]),e("h3",{attrs:{id:"release-的-force-废弃"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#release-的-force-废弃"}},[t._v("#")]),t._v(" "),e("code",[t._v("release")]),t._v(" 的 "),e("code",[t._v("--force")]),t._v(" 废弃")]),t._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[t._v("The "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--force")]),t._v(" flag has been deprecated\nIf you believe you have a valid reason to use the "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--force")]),t._v(" flag, please reach out to the Shorebird team by filing an issue at https://github.com/shorebirdtech/shorebird/issues/new\n")])])]),e("div",{staticClass:"language-diff extra-class"},[e("pre",{pre:!0,attrs:{class:"language-diff"}},[e("code",[e("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[e("span",{pre:!0,attrs:{class:"token prefix deleted"}},[t._v("-")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" shorebird release aar -f --release-version 7.0.0+1\n")])]),e("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[e("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),e("span",{pre:!0,attrs:{class:"token line"}},[t._v(" shorebird release aar --release-version 7.0.0+1\n")])])])])]),e("h3",{attrs:{id:"flutter-3-19-补丁"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#flutter-3-19-补丁"}},[t._v("#")]),t._v(" "),e("code",[t._v("Flutter 3.19")]),t._v(" 补丁")]),t._v(" "),e("p",[t._v("在【"),e("a",{attrs:{href:"https://juejin.cn/post/7349124917378695180",target:"_blank",rel:"noopener noreferrer"}},[t._v("Flutter - 升级3.19之后页面多次rebuild?🤨"),e("OutboundLink")],1),t._v("】一文中有讲到补丁需要去到 "),e("code",[t._v("Flutter")]),t._v(" 源码目录下执行。我们知道,"),e("code",[t._v("Shorebird")]),t._v(" 的热更新是魔改 "),e("code",[t._v("Flutter")]),t._v(" 实现的,所以我们就需要去找到用来打包时的魔改版 "),e("code",[t._v("Flutter")]),t._v(" 的目录位置。")]),t._v(" "),e("p",[t._v("下载的 "),e("code",[t._v("Flutter")]),t._v(" 源码统一存放在如下目录")]),t._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[t._v("~/.shorebird/bin/cache/flutter\n\n"),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(".")]),t._v("\n├── 5b9d29d67adb059103beefb65710ee3dabae2f85\n├── 771d07b2cf97cf107bae6eeedcf41bdc9db772fa\n├── a3d5f7c614aa1cc4d6cb1506e74fd1c81678e68e\n├── a6d1747d7f573b2ba2e2b96db1b76ed2f3f024da\n└── b9b23902966504a9778f4c07e3a3487fa84dcb2a\n")])])]),e("p",[t._v("但是目录中都是以 "),e("code",[t._v("hash")]),t._v(" 码命名的目录,要找到对应版本的目录,可以查看如下文件里的值")]),t._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[t._v("~/.shorebird/bin/internal/flutter.version\n\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# a6d1747d7f573b2ba2e2b96db1b76ed2f3f024da")]),t._v("\n")])])]),e("p",[t._v("如果原先的 "),e("code",[t._v("flutter versions use")]),t._v(" 还能用的话,一切就很简单了,先切到对应的 "),e("code",[t._v("Flutter")]),t._v(" 版本,然后 "),e("code",[t._v("cat")]),t._v(" 一下该文件,拿到 "),e("code",[t._v("hash")]),t._v(" 码就可以进入到对应版本的 "),e("code",[t._v("Flutter")]),t._v(" 目录,但是它已经被废弃了,所以得换另一个思路。")]),t._v(" "),e("p",[t._v("我们依旧可以通过 "),e("code",[t._v("flutter.version")]),t._v(" 拿到 "),e("code",[t._v("hash")]),t._v(" 码,但是它是当前最新受支持的 "),e("code",[t._v("Flutter")]),t._v(" 版本的,不过不慌,拿到后进入到该 "),e("code",[t._v("Flutter")]),t._v(" 目录,因为它是以 "),e("code",[t._v("git")]),t._v(" 的方式拉取下来的,所以有分支记录。")]),t._v(" "),e("p",[t._v("我们可以通过 "),e("code",[t._v("git rev-parse")]),t._v(" 命令拿到指定版本的 "),e("code",[t._v("hash")]),t._v("。")]),t._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 function"}},[t._v("git")]),t._v(" rev-parse "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--verify")]),t._v(" refs/remotes/origin/flutter_release/3.19.3\n\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 得到如下输出")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 0addba5b750987523c04ebe69e9755509369615a")]),t._v("\n")])])]),e("h2",{attrs:{id:"四、脚本使用-混编"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#四、脚本使用-混编"}},[t._v("#")]),t._v(" 四、脚本使用(混编)")]),t._v(" "),e("p",[t._v("专门服务于混编项目的 "),e("code",[t._v("Python")]),t._v(" 脚本,用于切换原生端集成 "),e("code",[t._v("Flutter")]),t._v(" 的方式(源码 或 二进制),以及封装 "),e("code",[t._v("Shorebird")]),t._v(" 命令的使用。")]),t._v(" "),e("blockquote",[e("p",[t._v("我们日常研发依旧使用的是源码依赖的方式,只会在打最终测试包时才需要通过脚本切到二进制依赖,并结合 "),e("code",[t._v("Jenkins")]),t._v(" 来辅助完成这种万年不变的无聊步骤~")])]),t._v(" "),e("p",[e("code",[t._v("Github")]),t._v(": "),e("a",{attrs:{href:"https://github.com/LinXunFeng/script_box/tree/main/flutter",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/LinXunFeng/script_box/tree/main/flutter"),e("OutboundLink")],1)]),t._v(" "),e("h3",{attrs:{id:"switch-flutter-integrate-py"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#switch-flutter-integrate-py"}},[t._v("#")]),t._v(" switch_flutter_integrate.py")]),t._v(" "),e("blockquote",[e("p",[t._v("切换 "),e("code",[t._v("Flutter")]),t._v(" 项目的集成方式")])]),t._v(" "),e("table",[e("thead",[e("tr",[e("th",[t._v("参数")]),t._v(" "),e("th",[t._v("描述")])])]),t._v(" "),e("tbody",[e("tr",[e("td",[e("code",[t._v("-p")])]),t._v(" "),e("td",[t._v("原生工程路径")])]),t._v(" "),e("tr",[e("td",[e("code",[t._v("-f")])]),t._v(" "),e("td",[t._v("平台("),e("code",[t._v("ios")]),t._v(" / "),e("code",[t._v("android")]),t._v(")")])])])]),t._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 comment"}},[t._v("# 二进制依赖")]),t._v("\npython switch_flutter_integrate.py "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-p")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'原生工程路径'")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-m")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'binary'")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'ios'")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 源码依赖")]),t._v("\npython switch_flutter_integrate.py "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-p")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'原生工程路径'")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-m")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'source'")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'ios'")]),t._v(" \n")])])]),e("h3",{attrs:{id:"shorebird-py"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#shorebird-py"}},[t._v("#")]),t._v(" shorebird.py")]),t._v(" "),e("blockquote",[e("p",[t._v("自动获取版本号,并执行 "),e("code",[t._v("Shorebird")]),t._v(" 相关命令")])]),t._v(" "),e("table",[e("thead",[e("tr",[e("th",[t._v("参数")]),t._v(" "),e("th",[t._v("描述")])])]),t._v(" "),e("tbody",[e("tr",[e("td",[e("code",[t._v("-p")])]),t._v(" "),e("td",[t._v("原生工程路径")])]),t._v(" "),e("tr",[e("td",[e("code",[t._v("-s")])]),t._v(" "),e("td",[e("code",[t._v("Flutter")]),t._v(" 工程路径")])]),t._v(" "),e("tr",[e("td",[e("code",[t._v("-m")])]),t._v(" "),e("td",[t._v("模式 ("),e("code",[t._v("release")]),t._v(" / "),e("code",[t._v("patch")]),t._v(")")])]),t._v(" "),e("tr",[e("td",[e("code",[t._v("-f")])]),t._v(" "),e("td",[t._v("平台("),e("code",[t._v("ios")]),t._v(" / "),e("code",[t._v("android")]),t._v(")")])]),t._v(" "),e("tr",[e("td",[e("code",[t._v("--flutter_version")])]),t._v(" "),e("td",[e("code",[t._v("Flutter")]),t._v(" 版本号,如:"),e("code",[t._v("3.19.3")])])]),t._v(" "),e("tr",[e("td",[e("code",[t._v("--patch_fix")])]),t._v(" "),e("td",[t._v("是否打 "),e("code",[t._v("3.19")]),t._v(" 补丁,如需要请传 "),e("code",[t._v("1")])])])])]),t._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 comment"}},[t._v("# release")]),t._v("\npython shorebird.py "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-p")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'原生工程路径'")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-s")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Flutter工程路径'")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-m")]),t._v(" release "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" ios\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 指定 Flutter 版本,以及打 3.19 的补丁")]),t._v("\npython shorebird.py "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-p")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'原生工程路径'")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-s")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Flutter工程路径'")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-m")]),t._v(" release "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" ios "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--flutter_version")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("3.19")]),t._v(".3 "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--patch_fix")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# patch")]),t._v("\npython shorebird.py "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-p")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'原生工程路径'")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-s")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Flutter工程路径'")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-m")]),t._v(" patch "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-f")]),t._v(" ios\n")])])])])}),[],!1,null,null,null);e.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/142.9f012dbb.js b/assets/js/142.9f012dbb.js new file mode 100644 index 000000000..1a6f57ea9 --- /dev/null +++ b/assets/js/142.9f012dbb.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[142],{454:function(t,s,a){"use strict";a.r(s);var n=a(8),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("iframe",{attrs:{src:"//player.bilibili.com/player.html?aid=1753525870&bvid=BV1mt421w73a&cid=1522045890&p=1",scrolling:"no",border:"0",frameborder:"no",framespacing:"0",allowfullscreen:"true",width:"100%",height:"500px"}}),t._v(" "),s("h2",{attrs:{id:"一、概述"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#一、概述"}},[t._v("#")]),t._v(" 一、概述")]),t._v(" "),s("p",[s("code",[t._v("Pigeon")]),t._v(" 是一个可以帮助我们生成 "),s("code",[t._v("Flutter")]),t._v(" 与 "),s("code",[t._v("原生")]),t._v(" 的通信代码的工具,我们只需要关注其两侧主要的数据处理逻辑即可,从而提升效率。")]),t._v(" "),s("p",[s("code",[t._v("Flutter")]),t._v(" 端对于视频缓存功能主要还是依赖原生端比较成熟的实现方案,如下两个开源库")]),t._v(" "),s("ul",[s("li",[t._v("iOS: "),s("a",{attrs:{href:"https://github.com/ChangbaDevs/KTVHTTPCache",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/ChangbaDevs/KTVHTTPCache"),s("OutboundLink")],1)]),t._v(" "),s("li",[t._v("安卓: "),s("a",{attrs:{href:"https://github.com/danikula/AndroidVideoCache",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/danikula/AndroidVideoCache"),s("OutboundLink")],1)])]),t._v(" "),s("p",[t._v("其功能是:丢给它一个视频链接,它将生成一个具备缓存功能的播放代理链接。")]),t._v(" "),s("p",[t._v("接下来我们一起看看,如何使用 "),s("code",[t._v("Pigeon")]),t._v(" 并结合上述两个库来实现视频缓存插件。")]),t._v(" "),s("h2",{attrs:{id:"二、创建-plugin"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#二、创建-plugin"}},[t._v("#")]),t._v(" 二、创建 "),s("code",[t._v("Plugin")])]),t._v(" "),s("p",[t._v("使用如下命令生成插件项目,这里我指定iOS使用的是 "),s("code",[t._v("Swift")]),t._v(",安卓使用的是 "),s("code",[t._v("Kotlin")])]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("flutter create "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--template")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("plugin "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--platforms")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("android,ios "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-i")]),t._v(" swift "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("-a")]),t._v(" kotlin 项目名\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 如:")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# flutter create --template=plugin --platforms=android,ios -i swift -a kotlin video_cache")]),t._v("\n")])])]),s("h2",{attrs:{id:"三、原生依赖"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#三、原生依赖"}},[t._v("#")]),t._v(" 三、原生依赖")]),t._v(" "),s("h3",{attrs:{id:"ios"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#ios"}},[t._v("#")]),t._v(" iOS")]),t._v(" "),s("p",[t._v("打开在 "),s("code",[t._v("ios")]),t._v(" 目录下的 "),s("code",[t._v("podspec")]),t._v(" 文件(这里是 "),s("code",[t._v("video_cache.podspec")]),t._v("),添加相关的第三方库依赖,比如我这里依赖的是 "),s("code",[t._v("KTVHTTPCache")]),t._v("。")]),t._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[t._v("#\n# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.\n# Run `pod lib lint video_cache.podspec` to validate before publishing.\n#\nPod::Spec.new do |s|\n"),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" s.name = 'video_cache'\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" s.version = '0.0.1'\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" s.summary = 'A new Flutter plugin project.'\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" s.description = <<-DESC\n")])]),t._v("A new Flutter plugin project.\n"),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" DESC\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" s.homepage = 'http://example.com'\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" s.license = { :file => '../LICENSE' }\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" s.author = { 'Your Company' => 'email@example.com' }\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" s.source = { :path => '.' }\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" s.source_files = 'Classes/**/*'\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" s.dependency 'Flutter'\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" s.platform = :ios, '11.0'\n")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" # Flutter.framework does not contain a i386 slice.\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" s.swift_version = '5.0'\n")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" # KTVHTTPCache\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" s.dependency 'KTVHTTPCache', '~> 3.0.0'\n")])]),t._v("end\n")])])]),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("android")]),t._v(" 目录下的 "),s("code",[t._v("build.gradle")]),t._v(" 文件,添加")]),t._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[t._v("...\nandroid {\n"),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" ...\n")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" dependencies {\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" testImplementation 'org.jetbrains.kotlin:kotlin-test'\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" testImplementation 'org.mockito:mockito-core:5.0.0'\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" implementation 'com.danikula:videocache:2.7.1'\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" }\n")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" ...\n")])]),t._v("}\n")])])]),s("p",[t._v("然后在 "),s("code",[t._v("example/android")]),t._v(" 目录下的 "),s("code",[t._v("build.gradle")]),t._v(" 和 "),s("code",[t._v("settings.gradle")]),t._v(" 文件添加如下 "),s("code",[t._v("maven")]),t._v(",否则会找不到依赖库")]),t._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[t._v("// build.gradle\n\nallprojects {\n"),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" repositories {\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(' maven { url "https://jitpack.io" }\n')]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" maven { url 'https://maven.aliyun.com/repository/public' }\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" google()\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" mavenCentral()\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" }\n")])]),t._v("}\n...\n")])])]),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[t._v("// settings.gradle\n\npluginManagement {\n"),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" ...\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" repositories {\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(' maven { url "https://jitpack.io" }\n')]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" maven { url 'https://maven.aliyun.com/repository/public' }\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" google()\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" mavenCentral()\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" gradlePluginPortal()\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" }\n")])]),t._v("}\n...\n")])])]),s("h2",{attrs:{id:"四、pigeon"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#四、pigeon"}},[t._v("#")]),t._v(" 四、Pigeon")]),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("pubspec.yaml")]),t._v(" 的 "),s("code",[t._v("dev_dependencies")]),t._v(" 下添加 "),s("code",[t._v("pigeon")]),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("dev_dependencies")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("pigeon")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" ^17.3.0\n")])])]),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("lib")]),t._v(" 目录外创建一个用来定义通信接口的 "),s("code",[t._v("dart")]),t._v(" 文件。")]),t._v(" "),s("p",[t._v("这里我们新建了一个与 "),s("code",[t._v("lib")]),t._v(" 目录同级的 "),s("code",[t._v("pigeons")]),t._v(" 文件夹,来存放与 "),s("code",[t._v("Pigeon")]),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 builtin class-name"}},[t._v(".")]),t._v("\n├── lib\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├── pigeons\n│ ├── cache.dart\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",[s("code",[t._v("cache.dart")]),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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:pigeon/pigeon.dart'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// https://github.com/flutter/packages/blob/main/packages/pigeon/example/README.md")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@ConfigurePigeon")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PigeonOptions")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n dartOut"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'lib/plugin/pigeon.g.dart'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n kotlinOut"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'android/src/main/kotlin/com/lxf/video_cache/VideoCacheGeneratedApis.g.kt'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n kotlinOptions"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("KotlinOptions")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// https://github.com/fluttercommunity/wakelock_plus/issues/18")]),t._v("\n errorClassName"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"LXFVideoCacheFlutterError"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 swiftOut"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'ios/Classes/LXFVideoCacheGeneratedApis.g.swift'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 metadata function"}},[t._v("@HostApi")]),s("span",{pre:!0,attrs:{class:"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("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("LXFVideoCacheHostApi")]),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("/// 转换为缓存代理URL")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("convertToCacheProxyUrl")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" url"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("h3",{attrs:{id:"生成交互代码"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#生成交互代码"}},[t._v("#")]),t._v(" 生成交互代码")]),t._v(" "),s("p",[t._v("再执行如下命令,指定根据 "),s("code",[t._v("cache.dart")]),t._v(" 来生成相应的繁杂且重要的交互代码。")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("flutter pub run pigeon "),s("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--input")]),t._v(" pigeons/cache.dart\n")])])]),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("kotlinOptions")]),t._v(" 里的 "),s("code",[t._v("errorClassName")]),t._v(",不然它会给你生成默认的 "),s("code",[t._v("FlutterError")]),t._v(",单单自己的插件编译可能不会怎样,但是一旦集成的项目里也有用到其它用 "),s("code",[t._v("Pigeon")]),t._v(" 生成了 "),s("code",[t._v("FlutterError")]),t._v(" 的插件时,就会报如下错误了")]),t._v(" "),s("div",{staticClass:"language-shell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-shell"}},[s("code",[t._v("Type FlutterError is defined multiple "),s("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("times")]),t._v("\n")])])]),s("p",[t._v("自定义 "),s("code",[t._v("kotlinOptions")]),t._v(" 里的 "),s("code",[t._v("errorClassName")]),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 function"}},[t._v("@ConfigurePigeon")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PigeonOptions")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 kotlinOptions"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("KotlinOptions")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// https://github.com/fluttercommunity/wakelock_plus/issues/18")]),t._v("\n errorClassName"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"LXFVideoCacheFlutterError"')])]),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("h2",{attrs:{id:"五、编写原生代码"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#五、编写原生代码"}},[t._v("#")]),t._v(" 五、编写原生代码")]),t._v(" "),s("h3",{attrs:{id:"ios-2"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#ios-2"}},[t._v("#")]),t._v(" iOS")]),t._v(" "),s("p",[t._v("进入到 "),s("code",[t._v("example/ios")]),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 builtin class-name"}},[t._v("cd")]),t._v(" example/ios \npod "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("install")]),t._v(" --repo-update\n")])])]),s("p",[t._v("使用 "),s("code",[t._v("Xcode")]),t._v(" 打开 "),s("code",[t._v("Runner.xcworkspace")]),t._v(" 开始编写原生代码")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// VideoCachePlugin.swift")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Flutter")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIKit")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("KTVHTTPCache")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 创建插件时自动生成的类")]),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("class")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VideoCachePlugin")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("NSObject")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterPlugin")]),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("public")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("static")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("register")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("with registrar"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterPluginRegistrar")]),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 class-name"}},[t._v("LXFVideoCacheHostApiSetup")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("setUp")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n binaryMessenger"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" registrar"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("messenger")]),s("span",{pre:!0,attrs:{class:"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 api"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFVideoCacheHostApiImplementation")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("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("LXFVideoCacheHostApiImplementation")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFVideoCacheHostApi")]),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("private")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" canProxy"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Bool")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v("\n \n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("convertToCacheProxyUrl")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("url"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("throws")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("->")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),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")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("canProxy "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),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("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("canProxy "),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("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("try")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("KTVHTTPCache")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("proxyStart")]),s("span",{pre:!0,attrs:{class:"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("!=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token nil constant"}},[t._v("nil")]),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 keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("canProxy"),s("span",{pre:!0,attrs:{class:"token operator"}},[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("return")]),t._v(" url "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 无法转 URL 对象")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("guard")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" urlObj "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("URL")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("string"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" url"),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(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" url "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n \n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("guard")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("let")]),t._v(" proxyUrlObj "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("KTVHTTPCache")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("proxyURL")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("withOriginalURL"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" urlObj"),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 comment"}},[t._v("// 代理失败")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" url\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 keyword"}},[t._v("return")]),t._v(" proxyUrlObj"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("absoluteString\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("h3",{attrs:{id:"安卓-2"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#安卓-2"}},[t._v("#")]),t._v(" 安卓")]),t._v(" "),s("p",[t._v("使用 "),s("code",[t._v("AndroidStudio")]),t._v(" 打开 "),s("code",[t._v("example/android")]),t._v(",找到外层的 "),s("code",[t._v("android")]),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("package")]),t._v(" com"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("lxf"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("video_cache\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" LXFVideoCacheHostApi\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" com"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("danikula"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("videocache"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("HttpProxyCacheServer\n\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("embedding"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("engine"),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("FlutterPlugin\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\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\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\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/** VideoCachePlugin */")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" VideoCachePlugin "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" FlutterPlugin"),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\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("private")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("lateinit")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" videoCacheHostApiImplementation"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" LXFVideoCacheHostApiImplementation\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("onAttachedToEngine")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("flutterPluginBinding"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" FlutterPlugin"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("FlutterPluginBinding"),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 videoCacheHostApiImplementation "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("LXFVideoCacheHostApiImplementation")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("flutterPluginBinding"),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 LXFVideoCacheHostApi"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("setUp")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n flutterPluginBinding"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("binaryMessenger"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n videoCacheHostApiImplementation"),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 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("onDetachedFromEngine")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("binding"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" FlutterPlugin"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("FlutterPluginBinding"),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 videoCacheHostApiImplementation"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("shutdown")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("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("onMethodCall")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("call"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" MethodCall"),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(" Result"),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("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 function"}},[t._v("LXFVideoCacheHostApiImplementation")]),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(" flutterPluginBinding"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" FlutterPlugin"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("FlutterPluginBinding\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" LXFVideoCacheHostApi "),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("private")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" cacheServer "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("by")]),t._v(" lazy "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" HttpProxyCacheServer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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("flutterPluginBinding"),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("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\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 重写并通过 cacheServer 将原 url 转换为具备缓存功能的 url")]),t._v("\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("convertToCacheProxyUrl")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("url"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" String"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" String "),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(" cacheServer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("getProxyUrl")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("url"),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("/// 关闭服务")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("fun")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("shutdown")]),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 cacheServer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("shutdown")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("h2",{attrs:{id:"六、开源库"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#六、开源库"}},[t._v("#")]),t._v(" 六、开源库")]),t._v(" "),s("p",[t._v("上述视频缓存插件已开源,并发布至 "),s("code",[t._v("GitHub")]),t._v(":"),s("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_video_cache",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/LinXunFeng/flutter_video_cache"),s("OutboundLink")],1)]),t._v(" "),s("p",[t._v("你可以通过如下步骤集成使用:")]),t._v(" "),s("p",[t._v("在 "),s("code",[t._v("pubspec.yaml")]),t._v(" 中添加 "),s("code",[t._v("video_cache")]),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 key atrule"}},[t._v("video_cache")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" latest_version\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 comment"}},[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-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:video_cache/video_cache.dart'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 将原视频链接转为缓存代理链接")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" url "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string-literal"}},[s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4'")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nurl "),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 class-name"}},[t._v("VideoCache")]),s("span",{pre:!0,attrs:{class:"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("convertToCacheProxyUrl")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("url"),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("// url转换结果")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// http://localhost:50050/https%3A%2F%2Fflutter%2Egithub%2Eio%2Fassets%2Dfor%2Dapi%2Ddocs%2Fassets%2Fvideos%2Fbee%2Emp4/KTVHTTPCachePlaceHolder/KTVHTTPCacheLastPathComponent.mp4")]),t._v("\n")])])]),s("p",[t._v("然后把转换后的 "),s("code",[t._v("url")]),t._v(" 丢给播放器就可以了~")]),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("Flutter")]),t._v(" 与原生交互拿到代理 "),s("code",[t._v("url")]),t._v(" 的例子,使用的是 "),s("code",[t._v("@HostApi")]),t._v(",而如果你如果在原生端去调用 "),s("code",[t._v("Flutter")]),t._v(" 的 "),s("code",[t._v("api")]),t._v(",则使用 "),s("code",[t._v("@FlutterApi")]),t._v(" 去标注相关抽象类即可,使用方法是差不多的。")]),t._v(" "),s("p",[t._v("需要注意的是,当你使用 "),s("code",[t._v("Swift")]),t._v(" 去写插件,且使用了 "),s("code",[t._v("@FlutterApi")]),t._v(" 去生成相应的原生代码后编译,可能会遇到这个错误")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("type 'FlutterError' does not conform to protocol 'Error'\n")])])]),s("p",[t._v("添加如下拓展即可")]),t._v(" "),s("div",{staticClass:"language-swift extra-class"},[s("pre",{pre:!0,attrs:{class:"language-swift"}},[s("code",[s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// https://github.com/flutter/flutter/issues/136081")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extension")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FlutterError")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Error")]),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("h2",{attrs:{id:"八、资料"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#八、资料"}},[t._v("#")]),t._v(" 八、资料")]),t._v(" "),s("ul",[s("li",[s("a",{attrs:{href:"https://github.com/flutter/packages/blob/main/packages/pigeon/example/README.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/flutter/packages/blob/main/packages/pigeon/example/README.md"),s("OutboundLink")],1)])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/143.64c7beef.js b/assets/js/143.64c7beef.js new file mode 100644 index 000000000..ec81d1407 --- /dev/null +++ b/assets/js/143.64c7beef.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[143],{456:function(t,a,s){"use strict";s.r(a);var e=s(8),n=Object(e.a)({},(function(){var t=this,a=t._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h2",{attrs:{id:"一、概述"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#一、概述"}},[t._v("#")]),t._v(" 一、概述")]),t._v(" "),a("p",[t._v("在常规的纯 "),a("code",[t._v("Flutter")]),t._v(" 项目中,我们只需要通过 "),a("code",[t._v("SystemChrome.setPreferredOrientations")]),t._v(" 方法就可以轻松驾驭屏幕旋转的需求,但相信很多还在整混编项目的老铁在 "),a("code",[t._v("iOS")]),t._v(" 上碰了壁,因为一般情况下,原生项目里都会把方向选项全部去掉,只保留 "),a("code",[t._v("Portrait")]),t._v(",如下图所示")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202406021457716.png",alt:""}})]),t._v(" "),a("p",[t._v("如果你原来就是一位 "),a("code",[t._v("iOS")]),t._v(" 开发者,那自然是小问题,通过 "),a("code",[t._v("MethodChannel")]),t._v(" 与原生进行通信,然后一顿代码输出就搞定了,但如果你不是 "),a("code",[t._v("iOS")]),t._v(" 开发者呢,那就有点无从下手了。")]),t._v(" "),a("p",[t._v("今天就跟大家分享一个我自己做的 "),a("code",[t._v("Flutter")]),t._v(" 屏幕旋转插件,只需几步即可轻松搞定这个功能。")]),t._v(" "),a("p",[t._v("等等,为啥不说安卓?那是因为 "),a("code",[t._v("SystemChrome.setPreferredOrientations")]),t._v(" 不管是纯 "),a("code",[t._v("Flutter")]),t._v(" 还是混编,对安卓一直都有效~")]),t._v(" "),a("p",[t._v("好吧,接下来就是手把手教学时刻。")]),t._v(" "),a("h2",{attrs:{id:"二、集成"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#二、集成"}},[t._v("#")]),t._v(" 二、集成")]),t._v(" "),a("p",[t._v("大胆的将 "),a("code",[t._v("switch_orientation")]),t._v(" 添加到你的 "),a("code",[t._v("pubspec.yaml")]),t._v(" 文件中")]),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("switch_orientation")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" latest_version\n")])])]),a("p",[t._v("具体版本大家到 "),a("code",[t._v("pub.dev")]),t._v(" 上复制粘贴最新的吧,附上链接:"),a("a",{attrs:{href:"https://pub.dev/packages/switch_orientation",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://pub.dev/packages/switch_orientation"),a("OutboundLink")],1)]),t._v(" "),a("h2",{attrs:{id:"三、配置"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#三、配置"}},[t._v("#")]),t._v(" 三、配置")]),t._v(" "),a("blockquote",[a("p",[t._v("安卓不需要配置,仅 "),a("code",[t._v("iOS")]),t._v(" 需要")])]),t._v(" "),a("h3",{attrs:{id:"swift-项目"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#swift-项目"}},[t._v("#")]),t._v(" "),a("code",[t._v("Swift")]),t._v(" 项目")]),t._v(" "),a("p",[t._v("如果你的原生项目是 "),a("code",[t._v("Swift")]),t._v(" 项目,那是最好了。")]),t._v(" "),a("p",[t._v("打开 "),a("code",[t._v("AppDelegate.swift")]),t._v(" 文件")]),t._v(" "),a("ul",[a("li",[t._v("导入 "),a("code",[t._v("LXFProtocolTool")])]),t._v(" "),a("li",[t._v("重写 "),a("code",[t._v("supportedInterfaceOrientationsForWindow")]),t._v(" 方法,并返回 "),a("code",[t._v("UIApplication.shared.lxf.currentVcOrientationMask")])])]),t._v(" "),a("p",[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("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFProtocolTool")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token attribute atrule"}},[t._v("@UIApplicationMain")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token attribute 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 class-name"}},[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 operator"}},[t._v("...")]),t._v("\n \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-definition function"}},[t._v("application")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token omit keyword"}},[t._v("_")]),t._v(" application"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIApplication")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" supportedInterfaceOrientationsFor window"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIWindow")]),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("->")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIInterfaceOrientationMask")]),t._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 class-name"}},[t._v("UIApplication")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("shared"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("lxf"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("currentVcOrientationMask\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:"oc-项目"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#oc-项目"}},[t._v("#")]),t._v(" "),a("code",[t._v("OC")]),t._v(" 项目")]),t._v(" "),a("p",[t._v("如果你的原生项目是 "),a("code",[t._v("OC")]),t._v(" 项目,则需要按如下步骤创建相应文件与添加代码")]),t._v(" "),a("p",[t._v("选择 "),a("code",[t._v("New File...")])]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202406021458258.png",alt:""}})]),t._v(" "),a("p",[t._v("选择 "),a("code",[t._v("Swift File")]),t._v(",点击 "),a("code",[t._v("Next")])]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202406021458830.png",alt:""}})]),t._v(" "),a("p",[t._v("文件名看你自己的用途,我这里是用来存储对 "),a("code",[t._v("AppDelegate")]),t._v(" 的拓展,所以命名为 "),a("code",[t._v("AppDelegate+Extension.swift")]),t._v(",点击 "),a("code",[t._v("Create")])]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202406021458344.png",alt:""}})]),t._v(" "),a("p",[t._v("接下来这一步很关键,一定要点击 "),a("code",[t._v("Create Bridging Header")]),t._v(" 来创建桥头文件,它的命名规则为 "),a("code",[t._v("项目名-Bridging-Header.h")]),t._v("。")]),t._v(" "),a("p",[t._v("当然,如果你的项目之前创建过桥头文件,它就不会有该提示了,跳过这一步即可~")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202406021458808.png",alt:""}})]),t._v(" "),a("p",[t._v("在 "),a("code",[t._v("AppDelegate+Extension.swift")]),t._v(" 文件中添加如下代码,供 "),a("code",[t._v("OC")]),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("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Foundation")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LXFProtocolTool")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extension")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AppDelegate")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token attribute atrule"}},[t._v("@objc")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("lxf_supportedInterfaceOrientations")]),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 class-name"}},[t._v("UIInterfaceOrientationMask")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("UIApplication")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("shared"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("lxf"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("currentVcOrientationMask\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("项目名-Bridging-Header.h")]),t._v(" 中添加如下代码")]),t._v(" "),a("blockquote",[a("p",[t._v("其作用就是让 "),a("code",[t._v("Swift")]),t._v(" 文件可以访问指定的 "),a("code",[t._v("OC")]),t._v(" 文件里的代码。")])]),t._v(" "),a("div",{staticClass:"language-objc extra-class"},[a("pre",{pre:!0,attrs:{class:"language-objc"}},[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 string"}},[t._v('"AppDelegate.h"')])]),t._v("\n")])])]),a("p",[t._v("回到 "),a("code",[t._v("AppDelegate.m")]),t._v(" 文件")]),t._v(" "),a("ul",[a("li",[t._v("导入 "),a("code",[t._v("项目名-Swift.h")]),t._v(",这样就可以在 "),a("code",[t._v("OC")]),t._v(" 中访问 "),a("code",[t._v("Swift")]),t._v(" 代码")]),t._v(" "),a("li",[t._v("在 "),a("code",[t._v("supportedInterfaceOrientationsForWindow")]),t._v(" 方法中调用 "),a("code",[t._v("lxf_supportedInterfaceOrientations")]),t._v(" 并将结果返回")])]),t._v(" "),a("p",[t._v("具体代码如下所示")]),t._v(" "),a("div",{staticClass:"language-objc extra-class"},[a("pre",{pre:!0,attrs:{class:"language-objc"}},[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 string"}},[t._v('"AppDelegate.h"')])]),t._v("\n"),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 string"}},[t._v('"LXFOCProject-Swift.h"')])]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("@interface")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("AppDelegate")]),t._v(" "),a("span",{pre:!0,attrs:{class:"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("@end")]),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 punctuation"}},[t._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 operator"}},[t._v("-")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("UIInterfaceOrientationMask"),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 supportedInterfaceOrientationsForWindow"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("UIWindow "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),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("\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("self")]),t._v(" lxf_supportedInterfaceOrientations"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{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("@end")]),t._v("\n")])])]),a("p",[t._v("OK,大功告成,接下来就可以愉快地去使用了~")]),t._v(" "),a("h2",{attrs:{id:"四、使用"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#四、使用"}},[t._v("#")]),t._v(" 四、使用")]),t._v(" "),a("p",[t._v("跟 "),a("code",[t._v("SystemChrome.setPreferredOrientations")]),t._v(" 的使用方式一模一样,只要将 "),a("code",[t._v("SystemChrome")]),t._v(" 替换成 "),a("code",[t._v("SwitchOrientation")]),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 class-name"}},[t._v("SwitchOrientation")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setPreferredOrientations")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DeviceOrientation")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("portraitUp"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("仅横屏")]),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 class-name"}},[t._v("SwitchOrientation")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setPreferredOrientations")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DeviceOrientation")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("landscapeLeft"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DeviceOrientation")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("landscapeRight"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("横竖屏")]),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 class-name"}},[t._v("SwitchOrientation")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setPreferredOrientations")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DeviceOrientation")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("portraitUp"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DeviceOrientation")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("portraitDown"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DeviceOrientation")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("landscapeLeft"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DeviceOrientation")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("landscapeRight"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("h2",{attrs:{id:"五、最后"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#五、最后"}},[t._v("#")]),t._v(" 五、最后")]),t._v(" "),a("p",[t._v("我已将上述的 "),a("code",[t._v("Flutter")]),t._v(" 屏幕旋转插件发布至 "),a("code",[t._v("GitHub")]),t._v(": "),a("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_switch_orientation",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/LinXunFeng/flutter_switch_orientation"),a("OutboundLink")],1)]),t._v(" "),a("p",[t._v("另外,如果你的原生项目有全屏旋转的需要,也可以继续使用我的原生库: "),a("a",{attrs:{href:"https://github.com/LinXunFeng/LXFProtocolTool",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/LinXunFeng/LXFProtocolTool"),a("OutboundLink")],1),t._v(",功能强大且简单易用。")]),t._v(" "),a("p",[t._v("好了,开源不易,如果你也觉得这个库好用,请不吝给个 "),a("code",[t._v("Star")]),t._v(" 👍")]),t._v(" "),a("p",[t._v("本篇到此结束,感谢大家的支持,我们下次再见! 👋")])])}),[],!1,null,null,null);a.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/144.cc019022.js b/assets/js/144.cc019022.js new file mode 100644 index 000000000..04abf3385 --- /dev/null +++ b/assets/js/144.cc019022.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[144],{455:function(t,a,s){"use strict";s.r(a);var n=s(8),e=Object(n.a)({},(function(){var t=this,a=t._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h2",{attrs:{id:"一、概述"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#一、概述"}},[t._v("#")]),t._v(" 一、概述")]),t._v(" "),a("p",[t._v("我们的 "),a("code",[t._v("App")]),t._v(" 是集成了 "),a("code",[t._v("Sentry")]),t._v(" 进行错误跟踪和性能监控,在最近几个月里经常看到如下错误")]),t._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[t._v("ClientException: Connection closed before full header was received, uri=https://xxx.png\n")])])]),a("p",[t._v("加载图片的时候报错了,但是尝试了很多次,自己始终无法复现出来~")]),t._v(" "),a("h2",{attrs:{id:"二、摸索"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#二、摸索"}},[t._v("#")]),t._v(" 二、摸索")]),t._v(" "),a("p",[t._v("后面进行搜索找到了 "),a("code",[t._v("2019")]),t._v(" 年 "),a("code",[t._v("12")]),t._v(" 月的一个 "),a("code",[t._v("issue")])]),t._v(" "),a("ul",[a("li",[t._v("https://github.com/flutter/flutter/issues/41573")])]),t._v(" "),a("p",[t._v("不太一样的是人家是请求接口的时候出现的报错。")]),t._v(" "),a("p",[t._v("提出 "),a("code",[t._v("issue")]),t._v(" 的作者当时是通过 "),a("code",[t._v("Method Channel")]),t._v(" 的方式由原生端去请求来解决这个问题,即避开了使用 "),a("code",[t._v("http")]),t._v(" 这个包。")]),t._v(" "),a("p",[t._v("有人说这种情况,改用 "),a("code",[t._v("dio")]),t._v(" 即可~")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202406042253062.png",alt:""}})]),t._v(" "),a("p",[t._v("也有人说这种情况,加个延迟就可以解决~")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202406042253818.png",alt:""}})]),t._v(" "),a("p",[t._v("但我们这种加载图片场景哪有机会加延迟呢,当然用这种方式也不靠谱~")]),t._v(" "),a("p",[t._v("时隔两年后("),a("code",[t._v("2021")]),t._v(" 年 "),a("code",[t._v("12")]),t._v(" 月),根据 "),a("code",[t._v("issue")]),t._v(" 的种种描述,有人确定了问题,并提了一个 "),a("code",[t._v("Dart")]),t._v(" 的 "),a("code",[t._v("issue")])]),t._v(" "),a("ul",[a("li",[t._v("https://github.com/dart-lang/sdk/issues/47841")])]),t._v(" "),a("p",[t._v("大致上就是说 "),a("code",[t._v("Dart")]),t._v(" 端拒绝了服务器发起的 "),a("code",[t._v("TLS")]),t._v(" 重协商导致。")]),t._v(" "),a("blockquote",[a("p",[t._v("重协商是指在已经协商好的SSL/TLS TCP连接上重新协商, 用以更换算法、更换数字证书、重新验证对方身份、更新共享密钥等。 --摘自《"),a("a",{attrs:{href:"https://www.cnblogs.com/blacksunny/articles/12185108.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("SSL/TLS攻击介绍--重协商漏洞攻击"),a("OutboundLink")],1),t._v("》")])]),t._v(" "),a("p",[t._v("我想 "),a("code",[t._v("Dart")]),t._v(" 不允许 "),a("code",[t._v("TLS")]),t._v(" 重协商应该也是出于安全考虑吧~")]),t._v(" "),a("h2",{attrs:{id:"三、解决"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#三、解决"}},[t._v("#")]),t._v(" 三、解决")]),t._v(" "),a("p",[t._v("提 "),a("code",[t._v("issue")]),t._v(" 的作者也为此,于 "),a("code",[t._v("2022")]),t._v(" 年 "),a("code",[t._v("2")]),t._v(" 月份提了 "),a("code",[t._v("PR")]),t._v(",给 "),a("code",[t._v("SecurityContext")]),t._v(" 添加了 "),a("code",[t._v("allowLegacyUnsafeRenegotiation")]),t._v(" 属性以允许重协商,在同年 "),a("code",[t._v("4")]),t._v(" 月份得到合并了,但依旧出于安全考虑,默认为关闭状态,是否开启由开发者自己决定。")]),t._v(" "),a("p",[t._v("附上该属性的相关说明:https://api.flutter.dev/flutter/dart-io/SecurityContext/allowLegacyUnsafeRenegotiation.html")]),t._v(" "),a("p",[t._v("以及相关修复:"),a("a",{attrs:{href:"https://github.com/dart-lang/sdk/commit/c286b76c2db394b72bd8ae79b32d024c2d25c52b#diff-6e59fb0cec49044e49bde0cd8d7a9d3614fa47575bb58c36db6b714b90edf559",target:"_blank",rel:"noopener noreferrer"}},[t._v("Allow sockets to enable TLS renegotiation. · dart-lang/sdk@c286b76 (github.com)"),a("OutboundLink")],1)]),t._v(" "),a("p",[t._v("注:该属性从 "),a("code",[t._v("Dart 2.17.0")]),t._v(" 开始存在,对应的 "),a("code",[t._v("Flutter")]),t._v(" 版本是 "),a("code",[t._v("3.0.0")]),t._v("。")]),t._v(" "),a("p",[t._v("加了 "),a("code",[t._v("allowLegacyUnsafeRenegotiation")]),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 keyword"}},[t._v("final")]),t._v(" context "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SecurityContext")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("defaultContext"),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("allowLegacyUnsafeRenegotiation "),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("final")]),t._v(" httpClient "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("HttpClient")]),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(" 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("final")]),t._v(" client "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("IOClient")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("httpClient"),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("await")]),t._v(" client"),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 class-name"}},[t._v("Uri")]),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("(")]),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'https://your_uri.net'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!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("代码取自:https://stackoverflow.com/a/73287131/8577739")]),t._v(" "),a("h2",{attrs:{id:"四、调整-cachednetworkimage"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#四、调整-cachednetworkimage"}},[t._v("#")]),t._v(" 四、调整 CachedNetworkImage")]),t._v(" "),a("p",[t._v("在我们的项目中,所使用的图片加载库为 "),a("code",[t._v("cached_network_image")]),t._v(",接下来我们就一起来看看如何调整以开启 "),a("code",[t._v("TLS")]),t._v(" 重协商功能。")]),t._v(" "),a("p",[t._v("在 "),a("code",[t._v("CachedNetworkImage")]),t._v(" 类中有一个 "),a("code",[t._v("cacheManager")]),t._v(" 属性,如果我们不设置的话则后续默认使用的是 "),a("code",[t._v("DefaultCacheManager")]),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("DefaultCacheManager")]),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("CacheManager")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ImageCacheManager")]),t._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(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" key "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'libCachedImageData'")])]),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("final")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DefaultCacheManager")]),t._v(" _instance "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DefaultCacheManager")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[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("factory")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DefaultCacheManager")]),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(" _instance"),a("span",{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 class-name"}},[t._v("DefaultCacheManager")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[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(" "),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 class-name"}},[t._v("Config")]),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(")")]),a("span",{pre:!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("CacheManager")])]),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("CacheManager")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("implements")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BaseCacheManager")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 punctuation"}},[t._v(".")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CacheManager")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Config")]),t._v(" config"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" _config "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" config"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n _store "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CacheStore")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("config"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n _webHelper "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WebHelper")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("_store"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" config"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("fileService"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!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("p",[a("code",[t._v("CacheManager")]),t._v(" 接收一个 "),a("code",[t._v("Config")]),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("abstract")]),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("Config")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("/// [fileService] defines where files are fetched, for example online.")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("factory")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Config")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" cacheKey"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Duration")]),t._v(" stalePeriod"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n int maxNrOfCacheObjects"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CacheInfoRepository")]),t._v(" repo"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FileSystem")]),t._v(" fileSystem"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FileService")]),t._v(" fileService"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._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 operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("impl"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")])]),t._v("Config")]),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(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FileService")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("get")]),t._v(" fileService"),a("span",{pre:!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("fileService")]),t._v(" 来指定 "),a("code",[t._v("httpClient")]),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("static")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("HttpClient")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("httpClient")]),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(" context "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SecurityContext")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("defaultContext"),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("allowLegacyUnsafeRenegotiation "),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("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("HttpClient")]),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(" 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\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 自定义 CacheManager")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" _LXFCachedNetworkImageManager "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CacheManager")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ImageCacheManager")]),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("// 为了不影响之前的缓存,保持使用相同的 key")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("static")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("get")]),t._v(" key "),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 class-name"}},[t._v("DefaultCacheManager")]),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("\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("final")]),t._v(" _LXFCachedNetworkImageManager _instance "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n _LXFCachedNetworkImageManager"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[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("factory")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("_LXFCachedNetworkImageManager")]),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(" _instance"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n _LXFCachedNetworkImageManager"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("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("super")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Config")]),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("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 指定 httpClient")]),t._v("\n fileService"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("HttpFileService")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n httpClient"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("IOClient")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("httpClient")]),a("span",{pre:!0,attrs:{class:"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(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!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("cacheManager")]),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 class-name"}},[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(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("''")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" \n cacheManager"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("_LXFCachedNetworkImageManager")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),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("p",[t._v("本篇到此结束,感谢大家的支持,我们下次再见! 👋")])])}),[],!1,null,null,null);a.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/145.caa7eeb2.js b/assets/js/145.caa7eeb2.js new file mode 100644 index 000000000..b4d883300 --- /dev/null +++ b/assets/js/145.caa7eeb2.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[145],{458:function(t,a,s){"use strict";s.r(a);var n=s(8),e=Object(n.a)({},(function(){var t=this,a=t._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("iframe",{attrs:{src:"//player.bilibili.com/player.html?isOutside=true&aid=112665531909649&bvid=BV1gAgreJE55&cid=500001593299517&p=1",scrolling:"no",border:"0",frameborder:"no",framespacing:"0",allowfullscreen:"true",width:"100%",height:"500px"}}),t._v(" "),a("h2",{attrs:{id:"一、概述"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#一、概述"}},[t._v("#")]),t._v(" 一、概述")]),t._v(" "),a("p",[t._v("相信大家接触【即时通信应用】的时候都会遇到这个问题,那就是如何去实现聊天页面的键盘和其它功能面板的丝滑切换,这种问题也有人向 "),a("code",[t._v("Flutter")]),t._v(" 官方提出过 "),a("code",[t._v("issue")]),t._v(",如下这两个:")]),t._v(" "),a("ul",[a("li",[a("a",{attrs:{href:"https://github.com/flutter/flutter/issues/121059",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/flutter/flutter/issues/121059"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://github.com/flutter/flutter/issues/32583",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/flutter/flutter/issues/32583"),a("OutboundLink")],1)])]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202406231252604.gif",alt:""}})]),t._v(" "),a("p",[t._v("可以看到,从表情面板切到文本输入框时会抖动一下,影响用户体验。")]),t._v(" "),a("p",[t._v("但几年过去了依旧没有得到解决,再加上我自己也有这个小烦恼,所以我与 "),a("a",{attrs:{href:"https://github.com/GitLqr",target:"_blank",rel:"noopener noreferrer"}},[t._v("GitLqr"),a("OutboundLink")],1),t._v("(GitHub: "),a("a",{attrs:{href:"https://github.com/GitLqr",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/GitLqr"),a("OutboundLink")],1),t._v(")一起开发了这个 "),a("code",[t._v("chat_bottom_container")]),t._v(",来帮助我们快速实现这个丝滑切换的效果。")]),t._v(" "),a("h2",{attrs:{id:"二、效果"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#二、效果"}},[t._v("#")]),t._v(" 二、效果")]),t._v(" "),a("p",[t._v("这里先附上效果图,好让大家直观感受一下~")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202406172255393.gif",alt:""}})]),t._v(" "),a("p",[t._v("下面我们一起来看看怎么使用吧。")]),t._v(" "),a("h2",{attrs:{id:"三、集成"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#三、集成"}},[t._v("#")]),t._v(" 三、集成")]),t._v(" "),a("p",[t._v("将 "),a("code",[t._v("chat_bottom_container")]),t._v(" 添加到你的 "),a("code",[t._v("pubspec.yaml")]),t._v(" 文件中")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v("dependencies"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n chat_bottom_container"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" latest_version\n")])])]),a("p",[t._v("具体版本大家到 "),a("code",[t._v("pub.dev")]),t._v(" 上复制粘贴最新的吧,附上链接:"),a("a",{attrs:{href:"https://pub.dev/packages/chat_bottom_container",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://pub.dev/packages/chat_bottom_container"),a("OutboundLink")],1)]),t._v(" "),a("p",[a("code",[t._v("Android")]),t._v(" 端需要添加 "),a("code",[t._v("jitpack")]),t._v(" 仓库到你的项目根目录下的 "),a("code",[t._v("build.gradle")]),t._v(" 文件中:")]),t._v(" "),a("div",{staticClass:"language-gradle extra-class"},[a("pre",{pre:!0,attrs:{class:"language-gradle"}},[a("code",[t._v("allprojects "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("repositories")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("...")]),t._v("\n maven "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" url "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'https://jitpack.io'")]),t._v(" "),a("span",{pre:!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("chat_bottom_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 keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:chat_bottom_container/chat_bottom_container.dart'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("h2",{attrs:{id:"四、使用"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#四、使用"}},[t._v("#")]),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 metadata function"}},[t._v("@override")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("build")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildContext")]),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("return")]),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 "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 设置为 false")]),t._v("\n resizeToAvoidBottomInset"),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 body"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),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 mainAxisAlignment"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[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 punctuation"}},[t._v("[")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[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 class-name"}},[t._v("ListView")]),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("\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 comment"}},[t._v("// 输入框视图")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildInputView")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"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("_buildPanelContainer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!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("底部容器放在最底下")]),t._v(" "),a("li",[a("code",[t._v("resizeToAvoidBottomInset")]),t._v(" 必须设置为 "),a("code",[t._v("false")])])]),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("chat_bottom_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 comment"}},[t._v("/// 自定义底部面板类型")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PanelType")]),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 none"),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 keyboard"),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 emoji"),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 tool"),a("span",{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("/// chat_bottom_container 的控制器,用来告知 chat_bottom_container 需要切面板类型了")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 注:这里传泛型传了 PanelType,是为了与外部的面板类型相关联")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" controller "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatBottomPanelContainerController")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PanelType")]),a("span",{pre:!0,attrs:{class:"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\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(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FocusNode")]),t._v(" inputFocusNode "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),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\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 记录当前的底部面板类型")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PanelType")]),t._v(" currentPanelType "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("none"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildPanelContainer")]),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 class-name"}},[t._v("ChatBottomPanelContainer")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),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 inputFocusNode"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" inputFocusNode"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n otherPanelWidget"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("type"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[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("type "),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("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[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 keyword"}},[t._v("switch")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("type"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[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(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("emoji"),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("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildEmojiPickerPanel")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"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("case")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("tool"),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("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildToolPanel")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"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("default")]),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 class-name"}},[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("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n onPanelTypeChange"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("panelType"),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(" "),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("// panelType: chat_bottom_container 内部定义的面板类型,就三种(.none|.keyboard|.other)")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// data: 外部自定义的底部面板类型")]),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("panelType"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[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(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatBottomPanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("none"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n currentPanelType "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("none"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),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(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatBottomPanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("keyboard"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n currentPanelType "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("keyboard"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),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(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatBottomPanelType")]),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("\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("data "),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("switch")]),t._v(" "),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(" "),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(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("emoji"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n currentPanelType "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("emoji"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),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(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("tool"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n currentPanelType "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("tool"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("default")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n currentPanelType "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("none"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),a("span",{pre:!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("break")]),a("span",{pre:!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 panelBgColor"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" panelBgColor"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!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("chat_bottom_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 keyword"}},[t._v("enum")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatBottomPanelType")]),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 none"),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 keyboard"),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 other"),a("span",{pre:!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("chat_bottom_container")]),t._v(" 并不关心你外部除了键盘外还有多少种面板类型,所以这里统一视为是 "),a("code",[t._v("ChatBottomPanelType.other")]),t._v("。")]),t._v(" "),a("p",[t._v("而我们外部开发者不可能不在乎,所以我们在点击表情面板按钮时,调用 "),a("code",[t._v("controller.updatePanelType")]),t._v(" 去切换底部面板类型,并为 "),a("code",[t._v("data")]),t._v(" 这个参数传入外部自定义的底部面板类型,如下代码所示")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v("controller"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("updatePanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 设置 ChatBottomPanelContainer 当前的底部面板类型")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 可传入 ChatBottomPanelType.keyboard | ChatBottomPanelType.other | ChatBottomPanelType.none")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatBottomPanelType")]),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("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 回调给外部开发者自定义的 PanelType,当 ChatBottomPanelType.other 时必传")]),t._v("\n data"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("emoji"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// PanelType.tool")]),t._v("\n"),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("otherPanelWidget")]),t._v(" 和 "),a("code",[t._v("onPanelTypeChange")]),t._v(" 回调中的类型,方便我们拿来使用。")]),t._v(" "),a("h2",{attrs:{id:"五、最后"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#五、最后"}},[t._v("#")]),t._v(" 五、最后")]),t._v(" "),a("p",[t._v("好了,上述便是该库的核心使用步骤, 我已将上述的 "),a("code",[t._v("Flutter")]),t._v(" 聊天底部面板容器库发布至 "),a("code",[t._v("GitHub")]),t._v(": "),a("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_chat_packages/tree/main/packages/chat_bottom_container",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/LinXunFeng/flutter_chat_packages/tree/main/packages/chat_bottom_container"),a("OutboundLink")],1),t._v(" ,如果接入上有什么问题,可以在链接中查看 "),a("code",[t._v("demo")]),t._v(" 演示代码。")]),t._v(" "),a("p",[t._v("开源不易,如果你也觉得这个库好用,请不吝给个 "),a("code",[t._v("Star")]),t._v(" 👍 ,并多多支持!")]),t._v(" "),a("p",[t._v("本篇到此结束,感谢大家的支持,我们下次再见! 👋")])])}),[],!1,null,null,null);a.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/146.e9731e8e.js b/assets/js/146.e9731e8e.js new file mode 100644 index 000000000..034f0508c --- /dev/null +++ b/assets/js/146.e9731e8e.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[146],{457:function(t,s,n){"use strict";n.r(s);var a=n(8),e=Object(a.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[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("scrollview_observer")]),t._v(" 功能的文章已过去了 "),s("code",[t._v("9个月")]),t._v(",目前 "),s("code",[t._v("scrollview_observer")]),t._v(" 也来到了 "),s("code",[t._v("1.21.0")]),t._v(" 版本,现在就带大家来看看都更新了哪些内容吧")]),t._v(" "),s("p",[t._v("GitHub: https://github.com/fluttercandies/flutter_scrollview_observer")]),t._v(" "),s("h2",{attrs:{id:"二、功能点"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#二、功能点"}},[t._v("#")]),t._v(" 二、功能点")]),t._v(" "),s("h3",{attrs:{id:"支持-nestedscrollview"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#支持-nestedscrollview"}},[t._v("#")]),t._v(" 支持 "),s("code",[t._v("NestedScrollView")])]),t._v(" "),s("p",[s("code",[t._v("scrollview_observer")]),t._v(" 的 "),s("code",[t._v("监听滚动视图中正在显示的子部件")]),t._v(" 与 "),s("code",[t._v("滚动到指定下标位置")]),t._v(" 这两大功能,现已对 "),s("code",[t._v("NestedScrollView")]),t._v(" 进行了支持,如下图所示")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202407061548786.gif",alt:""}})]),t._v(" "),s("blockquote",[s("p",[t._v("等下一篇文章再跟大家详细介绍使用步骤 : )")])]),t._v(" "),s("h3",{attrs:{id:"支持-center"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#支持-center"}},[t._v("#")]),t._v(" 支持 "),s("code",[t._v("center")])]),t._v(" "),s("p",[t._v("如下代码所示,在一些场景下,你可能会对 "),s("code",[t._v("CustomScrollView")]),t._v(" 的 "),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 class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildScrollView")]),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 class-name"}},[t._v("CustomScrollView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n center"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" _centerKey"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n anchor"),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 controller"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" scrollController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n slivers"),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("_buildSliverListView")]),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Colors")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("redAccent"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n onBuild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx"),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 _sliverListCtx1 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ctx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("_buildSliverListView")]),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Colors")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("blueGrey"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n onBuild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx"),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 _sliverListCtx2 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ctx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 comment"}},[t._v("// center")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverPadding")]),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[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(",")]),t._v(" key"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" _centerKey"),s("span",{pre:!0,attrs:{class:"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("_buildSliverListView")]),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Colors")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("teal"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n onBuild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx"),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 _sliverListCtx3 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ctx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("_buildSliverListView")]),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Colors")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("purple"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n onBuild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx"),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 _sliverListCtx4 "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ctx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("但又想使用下标跳转 "),s("code",[t._v("item")]),t._v(" 的功能,放心,"),s("code",[t._v("scrollview_observer")]),t._v(" 具有良好的兼容性,在 "),s("code",[t._v("1.19.1")]),t._v(" 版本下就已得到支持,可随便你如何设置 "),s("code",[t._v("center")]),t._v(",不需要额外做其它操作。")]),t._v(" "),s("h3",{attrs:{id:"observeintervalforscrolling"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#observeintervalforscrolling"}},[t._v("#")]),t._v(" "),s("code",[t._v("observeIntervalForScrolling")])]),t._v(" "),s("p",[t._v("相信大家都还记得,自动触发观察的时机有以下三种")]),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",[s("code",[t._v("scrollStart")])]),t._v(" "),s("td",[t._v("开始滚动")])]),t._v(" "),s("tr",[s("td",[s("code",[t._v("scrollUpdate")])]),t._v(" "),s("td",[t._v("滚动中")])]),t._v(" "),s("tr",[s("td",[s("code",[t._v("scrollEnd")])]),t._v(" "),s("td",[t._v("结束滚动")])])])]),t._v(" "),s("p",[t._v("其中 "),s("code",[t._v("scrollUpdate")]),t._v(" 触发观察太过于频繁,其实很多次观察结果并不会相差多少,在大多数使用场景下,对我们来说也不太重要。")]),t._v(" "),s("p",[t._v("为此在本次更新中为 "),s("code",[t._v("ObserverController")]),t._v(" 新增了 "),s("code",[t._v("observeIntervalForScrolling")]),t._v(" 属性,用来设置触发观察的间隔,从而大量减少不必要的观察计算。")]),t._v(" "),s("p",[t._v("需要注意以下两点:")]),t._v(" "),s("ul",[s("li",[t._v("为了不改变之前的行为,所以默认值为 "),s("code",[t._v("Duration.zero")]),t._v(",所以大家需要自行调整,推荐设置为 "),s("code",[t._v("Duration(milliseconds: 500)")]),t._v("。")]),t._v(" "),s("li",[t._v("该属性仅对 "),s("code",[t._v("scrollUpdate")]),t._v(" 有效。")])]),t._v(" "),s("h3",{attrs:{id:"visiblemainaxissize"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#visiblemainaxissize"}},[t._v("#")]),t._v(" "),s("code",[t._v("visibleMainAxisSize")])]),t._v(" "),s("blockquote",[s("p",[s("code",[t._v("item")]),t._v(" 显示的尺寸大小")])]),t._v(" "),s("p",[t._v("如图所示,各固定 "),s("code",[t._v("200")]),t._v(" 高度的 "),s("code",[t._v("item")]),t._v(" 在 "),s("code",[t._v("ListView")]),t._v(" 中的 "),s("code",[t._v("visibleMainAxisSize")]),t._v("。")]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202407061548544.png",alt:""}})]),t._v(" "),s("h3",{attrs:{id:"visiblefraction"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#visiblefraction"}},[t._v("#")]),t._v(" "),s("code",[t._v("visibleFraction")])]),t._v(" "),s("blockquote",[s("p",[t._v("在 "),s("code",[t._v("sliver")]),t._v(" 中 "),s("code",[t._v("item")]),t._v(" 的显示占比")]),t._v(" "),s("p",[t._v("计算公式:visibleFraction = visibleMainAxisSize / paintExtent")])]),t._v(" "),s("p",[s("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202407061548812.png",alt:""}})]),t._v(" "),s("p",[t._v("如图所示,当前 "),s("code",[t._v("SliverList")]),t._v(" 的绘制长度 "),s("code",[t._v("paintExtent")]),t._v(" 为 "),s("code",[t._v("376")]),t._v(",其 "),s("code",[t._v("item20")]),t._v(" 的可视大小 "),s("code",[t._v("visibleMainAxisSize")]),t._v(" 为 "),s("code",[t._v("30")]),t._v(",所以 "),s("code",[t._v("item20")]),t._v(" 的可视占比 "),s("code",[t._v("visibleFraction")]),t._v(" 为 "),s("code",[t._v("30/376")]),t._v("。")]),t._v(" "),s("h3",{attrs:{id:"sliverobservecontext"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#sliverobservecontext"}},[t._v("#")]),t._v(" "),s("code",[t._v("SliverObserveContext")])]),t._v(" "),s("p",[t._v("用于获取 "),s("code",[t._v("sliver")]),t._v(" 的 "),s("code",[t._v("BuildContext")]),t._v(",这在观察 "),s("code",[t._v("sliver")]),t._v(" 的场景下非常有用。")]),t._v(" "),s("p",[t._v("如下 "),s("code",[t._v("CustomScrollView")]),t._v(" 配置了多个 "),s("code",[t._v("sliver")])]),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 class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildScrollView")]),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 class-name"}},[t._v("CustomScrollView")]),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(" scrollController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n physics"),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 class-name"}},[t._v("ClampingScrollPhysics")]),s("span",{pre:!0,attrs:{class:"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 slivers"),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("// banner")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverPersistentHeader")]),s("span",{pre:!0,attrs:{class:"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(")")]),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 class-name"}},[t._v("SliverObserveContextToBoxAdapter")]),s("span",{pre:!0,attrs:{class:"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(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// tabBar")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverPersistentHeader")]),s("span",{pre:!0,attrs:{class:"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(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 构建多个 SliverGird")]),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("List"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("generate")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("modelList"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("length"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("mainIndex"),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("_buildSectionGridView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("mainIndex"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("我们需要观察当前哪个 "),s("code",[t._v("SliverGrid")]),t._v(" 是第一个,然后去同步更新 "),s("code",[t._v("TabBar")]),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("/// 记录各 Sliver 的下标与 BuildContext")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Map")]),s("span",{pre:!0,attrs:{class:"token generics"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("int"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildContext")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" sliverIndexCtxMap "),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("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverViewObserver")]),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(" sliverObserverController"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sliverContexts"),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 operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" sliverIndexCtxMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("values"),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 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 onObserveViewport"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),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(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("p",[t._v("但在研发过程中,我们很有可能会给 "),s("code",[t._v("SliverGrid")]),t._v(" 再加一层 "),s("code",[t._v("Sliver")]),t._v(" 去添加装饰、间距等,而 "),s("code",[t._v("onObserveViewport")]),t._v(" 只认最外层的 "),s("code",[t._v("sliver")]),t._v(",所以在这里我们就用 "),s("code",[t._v("SliverObserveContext")]),t._v(" 去进行包裹成为最外层,在其 "),s("code",[t._v("onObserve")]),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 class-name"}},[t._v("Widget")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildSectionGridView")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("int mainIndex"),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("Widget")]),t._v(" resultWidget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverGrid")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n gridDelegate"),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 class-name"}},[t._v("SliverGridDelegateWithFixedCrossAxisCount")]),s("span",{pre:!0,attrs:{class:"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(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n delegate"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverChildBuilderDelegate")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildContext")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" int index"),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("// 拿到了 SliverGrid 的 BuildContext")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),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 "),s("span",{pre:!0,attrs:{class:"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 childCount"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[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 punctuation"}},[t._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 resultWidget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverPadding")]),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(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[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")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sliver"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" resultWidget"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 comment"}},[t._v("// 在最外层使用 SliverObserveContext 进行包裹,以获取是外层的 BuildContext。")]),t._v("\n resultWidget "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SliverObserveContext")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n onObserve"),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 sliverIndexCtxMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("mainIndex"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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(" resultWidget"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("return")]),t._v(" resultWidget"),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("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 keyword"}},[t._v("enum")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatScrollObserverHandleMode")]),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 comment"}},[t._v("/// 来一条消息就插入一条消息")]),t._v("\n normal"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 生成式模式")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 比如 ChatGPT 这种流式自更新的消息")]),t._v("\n generative"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 指定模式")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 可以灵活指定用来做为参照的消息item")]),t._v("\n specified"),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("refItemRelativeIndex")]),t._v(" 和 "),s("code",[t._v("refItemRelativeIndexAfterUpdate")]),t._v(" 两个参数仅能表达相对下标之意,而无法表达参考坐标系,所以将其废弃。")]),t._v(" "),s("p",[t._v("新增 "),s("code",[t._v("refItemIndex")]),t._v(" 与 "),s("code",[t._v("refItemIndexAfterUpdate")]),t._v(",并结合 "),s("code",[t._v("refIndexType")]),t._v(" 来更好地指定参考 "),s("code",[t._v("item")]),t._v("。")]),t._v(" "),s("p",[t._v("我们先来看一下 "),s("code",[t._v("refIndexType")]),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("enum")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatScrollObserverRefIndexType")]),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("/// relativeIndex trailing")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("///")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 6 | item16 | cacheExtent")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// ----------------- -----------------")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 5 | item15 |")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 4 | item14 |")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 3 | item13 | 正在显示中的item")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 2 | item12 |")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 1 | item11 |")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// ----------------- -----------------")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 0 | item10 | cacheExtent <---- start")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("///")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// leading")]),t._v("\n relativeIndexStartFromCacheExtent"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// relativeIndex trailing")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("///")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 5 | item16 | cacheExtent")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// ----------------- -----------------")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 4 | item15 |")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 3 | item14 |")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 2 | item13 | 正在显示中的item")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 1 | item12 |")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 0 | item11 | <---- start")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// ----------------- -----------------")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// -1 | item10 | cacheExtent")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("///")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// leading")]),t._v("\n relativeIndexStartFromDisplaying"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 直接指定 item 的下标")]),t._v("\n itemIndex"),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("3种")]),t._v(" 参考模式供你选择")]),t._v(" "),s("ul",[s("li",[s("code",[t._v("relativeIndexStartFromCacheExtent")]),t._v(": 从渲染区的 "),s("code",[t._v("item")]),t._v(" 开始计算下标。这种模式一般用于普通消息插入,因为插入消息必定是在 "),s("code",[t._v("0")]),t._v(" 处,插入消息前后不变的就是原来的最新消息,其下标从 "),s("code",[t._v("0")]),t._v(" 变成了 "),s("code",[t._v("1")]),t._v(",此时 "),s("code",[t._v("refItemIndex")]),t._v(" 可指定为 "),s("code",[t._v("0")]),t._v(",而 "),s("code",[t._v("refItemIndexAfterUpdate")]),t._v(" 指定为 "),s("code",[t._v("1")]),t._v("。")]),t._v(" "),s("li",[s("code",[t._v("relativeIndexStartFromCacheExtent")]),t._v(": 从展示区的 "),s("code",[t._v("item")]),t._v(" 开始计算下标。该模式比较少用,一般是结合观察功能,因为通过观察功能,我们是可以轻松得知正在显示的 "),s("code",[t._v("item")]),t._v(" 有哪些,假设你此时对正在显示的第一个 "),s("code",[t._v("item")]),t._v(" 做了内容变更,但又不想影响第二个正在显示的 "),s("code",[t._v("item")]),t._v(" 的偏移,那这个模式正好适合当前这种的场景。因为改变前后不变的是第二个正在显示的 "),s("code",[t._v("item")]),t._v(",所以 "),s("code",[t._v("refItemIndex")]),t._v(" 指定为 "),s("code",[t._v("1")]),t._v(", "),s("code",[t._v("refItemIndexAfterUpdate")]),t._v(" 也指定为 "),s("code",[t._v("1")]),t._v("。")]),t._v(" "),s("li",[s("code",[t._v("itemIndex")]),t._v(": 三种模式中最容易理解的模式,用来参照的 "),s("code",[t._v("item")]),t._v(" 的下标是什么,你就指定什么,比如上述中 "),s("code",[t._v("item11")]),t._v(" 发生了变化,我们想保持位置就可以拿不变的 "),s("code",[t._v("item12")]),t._v(" 来做参照,所以 "),s("code",[t._v("refItemIndex")]),t._v(" 和 "),s("code",[t._v("refItemIndexAfterUpdate")]),t._v(" 都指定为 "),s("code",[t._v("12")]),t._v("。")])]),t._v(" "),s("p",[t._v("记住,不管你选择哪种参考模式,都需要注意的一点,即指定的参照 "),s("code",[t._v("item")]),t._v(" 需要在变化前后都有被渲染,这样才能确保保持位置的功能可以正常生效!")]),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("scrollview_observer")]),t._v(" 的使用又更加清楚,开源不易,如果你也觉得这个库好用,请不吝给个 "),s("code",[t._v("Star")]),t._v(" 👍")]),t._v(" "),s("p",[t._v("GitHub: "),s("a",{attrs:{href:"https://github.com/fluttercandies/flutter_scrollview_observer",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/fluttercandies/flutter_scrollview_observer"),s("OutboundLink")],1)]),t._v(" "),s("p",[t._v("本篇到此结束,感谢大家的支持,我们下次再见! 👋")])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/147.35098ba3.js b/assets/js/147.35098ba3.js new file mode 100644 index 000000000..8038d99c6 --- /dev/null +++ b/assets/js/147.35098ba3.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[147],{459:function(t,a,s){"use strict";s.r(a);var n=s(8),e=Object(n.a)({},(function(){var t=this,a=t._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h2",{attrs:{id:"一、概述"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#一、概述"}},[t._v("#")]),t._v(" 一、概述")]),t._v(" "),a("p",[t._v("距离 "),a("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_chat_packages/tree/main/packages/chat_bottom_container",target:"_blank",rel:"noopener noreferrer"}},[t._v("chat_bottom_container"),a("OutboundLink")],1),t._v(" 首个可用版本 ("),a("code",[t._v("0.0.2")]),t._v(") 的发布已经过去了 "),a("code",[t._v("1")]),t._v(" 个多月,在这期间根据大家的使用反馈,我们也做了一些优化调整,今天就来盘点一下到底做了哪些优化,新增了什么功能,以及一些常见操作。")]),t._v(" "),a("p",[t._v("请注意")]),t._v(" "),a("ul",[a("li",[t._v("本篇仅介绍更新的优化及功能,基础使用请查看: "),a("RouterLink",{attrs:{to:"/pages/1af1e6/"}},[t._v("Flutter - 实现聊天键盘与功能面板的丝滑切换 🍻")])],1),t._v(" "),a("li",[t._v("截至本篇发布时,最新版本为 "),a("code",[t._v("0.2.0")]),t._v(",可以关注我们的微信公众号 "),a("a",{attrs:{href:"https://fastly.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/FSA_QR_bottom.png",target:"_blank",rel:"noopener noreferrer"}},[t._v("FSA全栈行动"),a("OutboundLink")],1),t._v(" 获取最新的资讯")])]),t._v(" "),a("blockquote",[a("p",[t._v("开源不易,如果你也觉得这个库好用,请不吝给个 "),a("code",[t._v("Star")]),t._v(" 👍 ,并多多支持!")]),t._v(" "),a("p",[a("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_chat_packages",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/LinXunFeng/flutter_chat_packages"),a("OutboundLink")],1)])]),t._v(" "),a("h2",{attrs:{id:"二、使用"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#二、使用"}},[t._v("#")]),t._v(" 二、使用")]),t._v(" "),a("h3",{attrs:{id:"调整键盘高度监听管理逻辑"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#调整键盘高度监听管理逻辑"}},[t._v("#")]),t._v(" 调整键盘高度监听管理逻辑")]),t._v(" "),a("p",[a("code",[t._v("0.1.0")]),t._v(" 版本前,只考虑了页面栈这种常规情况,当键盘高度变化时只处理栈顶的监听。")]),t._v(" "),a("p",[t._v("但其实还有一种常见打破该规则的场景,就是悬浮聊天页,它会一直在页面上,可能为了能快速从悬浮小球展开聊天页面,收起时只是做了隐藏,而不会销毁页面,在这种情况下,它依旧在监听管理里的栈顶,所以在收起后,上一个聊天页的键盘高度监听就会失效。")]),t._v(" "),a("p",[t._v("这个在 "),a("code",[t._v("0.1.0")]),t._v(" 版本中得到修复,内部会倒序遍历调用所有的监听回调。")]),t._v(" "),a("p",[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("0")]),t._v(",这里可以用 "),a("code",[t._v("iOS")]),t._v(" 模拟器结合 "),a("code",[t._v("Toggle Software Keyboard")]),t._v(" (快捷键: "),a("code",[t._v("cmd + k")]),t._v(") 来模拟连接与断开外接键盘的效果。")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202408041319269.gif",alt:""}})]),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("让输入框失去焦点")]),t._v(" "),a("li",[t._v("更新内部状态为 "),a("code",[t._v("ChatBottomPanelType.none")])])]),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("hidePanel")]),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("// 0.2.0 前")]),t._v("\n inputFocusNode"),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 keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatBottomPanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("none "),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("currentPanelType"),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 controller"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("updatePanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatBottomPanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("none"),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("// 0.2.0 后,可以这么写")]),t._v("\n controller"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("updatePanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatBottomPanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("none"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n forceHandleFocus"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatBottomHandleFocus")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("unfocus"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!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("chat_bottom_container")]),t._v(" 在收起模式 ("),a("code",[t._v(".none")]),t._v(") 下会自动帮你添加底部安全区高度,但在一些场景下你可能不希望如此。比如:")]),t._v(" "),a("ul",[a("li",[t._v("安卓的底部安全区的高度,很多小伙伴都是简单粗暴的设置个高度了事")]),t._v(" "),a("li",[a("code",[t._v("App")]),t._v(" 首页有底部 "),a("code",[t._v("BottomNavigationBar")]),t._v(",不需要安全区高度")])]),t._v(" "),a("p",[t._v("在此,你可以通过将 "),a("code",[t._v("safeAreaBottom")]),t._v(" 参数来自定义这个高度,如下设置为 "),a("code",[t._v("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 keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatBottomPanelContainer")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 safeAreaBottom"),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 "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\n"),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("p",[t._v("如示例中位于首页的聊天页面")]),t._v(" "),a("img",{attrs:{height:"750",src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202408041319288.png"}}),t._v(" "),a("p",[t._v("在键盘弹出时,如下图所示")]),t._v(" "),a("table",[a("thead",[a("tr",[a("th",[t._v("实际")]),t._v(" "),a("th",[t._v("期望")])])]),t._v(" "),a("tbody",[a("tr",[a("td",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202408041319353.png",alt:""}})]),t._v(" "),a("td",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202408041319990.png",alt:""}})])])])]),t._v(" "),a("p",[t._v("很明显,我们希望键盘容器高度能够减去外层底部固定的 "),a("code",[t._v("BottomNavigationBar")]),t._v(" 高度。")]),t._v(" "),a("p",[a("code",[t._v("ChatBottomPanelContainer")]),t._v(" 提供了 "),a("code",[t._v("changeKeyboardPanelHeight")]),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("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatBottomPanelContainer")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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 changeKeyboardPanelHeight"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("keyboardHeight"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[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(" renderObj "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" bottomNavigationBarKey"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("currentContext"),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("findRenderObject")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"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("renderObj "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("is!")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("RenderBox")]),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(" keyboardHeight"),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(" keyboardHeight "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v(" renderObj"),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("height"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("h3",{attrs:{id:"缓存键盘高度"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#缓存键盘高度"}},[t._v("#")]),t._v(" 缓存键盘高度")]),t._v(" "),a("p",[t._v("先来看未做键盘高度缓存处理之前,会发生什么?")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20230813121546/image/202408041320403.gif",alt:""}})]),t._v(" "),a("p",[t._v("上图一共进入了三次聊天页")]),t._v(" "),a("ul",[a("li",[t._v("第一次是先点击键盘,再切到表情面板,体验起来还是挺不错的。")]),t._v(" "),a("li",[t._v("为了避免一闪而过,没有注意到,所以第二次和第三次的操作是一样的,先唤起表情面板,再切到键盘,可以看到在切到键盘时会抖动。")])]),t._v(" "),a("p",[t._v("这是因为每次进入聊天页,键盘的高度为初始值 "),a("code",[t._v("0")]),t._v(",在 "),a("code",[t._v("0.2.0")]),t._v(" 版本中对此进行了优化,加入了键盘高度缓存逻辑,从而尽量避免该抖动问题的出现。")]),t._v(" "),a("blockquote",[a("p",[t._v("❗️ 但需要注意的是,假如你卸载重装 "),a("code",[t._v("App")]),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 class-name"}},[t._v("Widget")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("_buildEmojiPickerPanel")]),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("// 如果键盘高度还没有缓存过,则使用默认高度 300")]),t._v("\n double height "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("300")]),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(" keyboardHeight "),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("keyboardHeight"),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("keyboardHeight "),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 height "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" keyboardHeight"),a("span",{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 class-name"}},[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(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("EdgeInsets")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("zero"),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 color"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[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("50")]),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("const")]),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 class-name"}},[t._v("Text")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Emoji Panel'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!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/FullStackAction/PicBed@resource20230813121546/image/202408041320246.gif",alt:""}})]),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("p",[t._v("先按如下设置你的输入框")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v("bool readOnly "),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 class-name"}},[t._v("TextEditingController")]),t._v(" textEditingController "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),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\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 class-name"}},[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(" textEditingController"),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(" inputFocusNode"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 为 true 时不显示键盘,默认为 false")]),t._v("\n readOnly"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" readOnly"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 获取焦点后显示光标,设置为 true 才不受 readOnly 的影响")]),t._v("\n showCursor"),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(".")]),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("switchToEmojiPanel")]),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 readOnly "),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 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\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 等待下一帧")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("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("addPostFrameCallback")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),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 punctuation"}},[t._v("{")]),t._v("\n controller"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("updatePanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 内部切至 other 状态")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatBottomPanelType")]),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("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[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 class-name"}},[t._v("PanelType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("emoji"),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 forceHandleFocus"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ChatBottomHandleFocus")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("requestFocus"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._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("p",[t._v("在 "),a("code",[t._v("updatePanelType")]),t._v(" 方法中,如果是切至 "),a("code",[t._v(".other")]),t._v(" 状态,是会帮你执行失去焦点操作的,所以这里提供了一个 "),a("code",[t._v("forceHandleFocus")]),t._v(" 参数,如果你对方法内部对焦点的处理不满意,你可以使用它来强制指定焦点的处理方式。")]),t._v(" "),a("h2",{attrs:{id:"三、最后"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#三、最后"}},[t._v("#")]),t._v(" 三、最后")]),t._v(" "),a("p",[t._v("好了,上述便是该库的更新内容, 惯例附上 "),a("code",[t._v("GitHub")]),t._v(" 地址: "),a("a",{attrs:{href:"https://github.com/LinXunFeng/flutter_chat_packages/tree/main/packages/chat_bottom_container",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/LinXunFeng/flutter_chat_packages/tree/main/packages/chat_bottom_container"),a("OutboundLink")],1),t._v(" ,如果接入上有什么问题,可以在链接中查看 "),a("code",[t._v("demo")]),t._v(" 演示代码。")]),t._v(" "),a("p",[t._v("开源不易,如果你也觉得这个库好用,请不吝给个 "),a("code",[t._v("Star")]),t._v(" 👍 ,并多多支持!")]),t._v(" "),a("p",[t._v("本篇到此结束,感谢大家的支持,我们下次再见! 👋")])])}),[],!1,null,null,null);a.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/148.b9c706c3.js b/assets/js/148.b9c706c3.js new file mode 100644 index 000000000..a6330b922 --- /dev/null +++ b/assets/js/148.b9c706c3.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[148],{460:function(t,s,a){"use strict";a.r(s);var n=a(8),p=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h2",{attrs:{id:"一、简述"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#一、简述"}},[t._v("#")]),t._v(" 一、简述")]),t._v(" "),s("p",[t._v("公司最近做的一个官网项目,要求把页面成国际化,实现中英文切换,为此,我在网上找了一些中英文切换的解决方案,大概为如下两种:")]),t._v(" "),s("h3",{attrs:{id:"_1、使用谷歌整站翻译api"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、使用谷歌整站翻译api"}},[t._v("#")]),t._v(" 1、使用谷歌整站翻译Api")]),t._v(" "),s("ul",[s("li",[t._v("优点:只须调用接口,即可轻松完成整站翻译,翻译准确度还行。")]),t._v(" "),s("li",[t._v("缺点:需要梯子。")]),t._v(" "),s("li",[t._v("参考文章:"),s("a",{attrs:{href:"http://www.cgtblog.com/qd/211.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("js代码实现网站中英文相互翻译"),s("OutboundLink")],1)])]),t._v(" "),s("h3",{attrs:{id:"_2、自己编写中英文对照表-用js控制"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、自己编写中英文对照表-用js控制"}},[t._v("#")]),t._v(" 2、自己编写中英文对照表,用js控制")]),t._v(" "),s("ul",[s("li",[t._v("优点:一对一翻译,所以准确度最高。")]),t._v(" "),s("li",[t._v("缺点:需要编写大量中英文对照表,只适合于少量的固定翻译。")]),t._v(" "),s("li",[t._v("参考文章:"),s("a",{attrs:{href:"http://blog.csdn.net/stpeace/article/details/10858171",target:"_blank",rel:"noopener noreferrer"}},[t._v("html页面如何实现中英文切换?"),s("OutboundLink")],1)])]),t._v(" "),s("p",[t._v("由于公司的要求是把整站进行翻译,且官网中存在新闻,即存在大量不可预料的数据,同时考虑到富文本数据不好翻译。所以,总的来说,第一种方案最为合适,不过可惜,需要梯子,没办法,只能别寻出路。好在微软也有一个类似谷歌整站翻译的Api:")]),t._v(" "),s("blockquote",[s("p",[s("a",{attrs:{href:"https://msdn.microsoft.com/en-us/library/dn341982.aspx",target:"_blank",rel:"noopener noreferrer"}},[t._v(" The Translator Web Widget API"),s("OutboundLink")],1)])]),t._v(" "),s("p",[t._v("其实,微软提供的Demo实现上也很简单,分如下几步:")]),t._v(" "),s("ol",[s("li",[t._v("引入The Translator Web Widget API")]),t._v(" "),s("li",[t._v("监听dom加载完毕,调用Microsoft.Translator.Widget.Translate()整站翻译。")])]),t._v(" "),s("h2",{attrs:{id:"二、实现"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#二、实现"}},[t._v("#")]),t._v(" 二、实现")]),t._v(" "),s("h3",{attrs:{id:"_1、封装language-js"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1、封装language-js"}},[t._v("#")]),t._v(" 1、封装language.js")]),t._v(" "),s("p",[t._v("根据上面的对微软提供的Demo的研究,我们对此进一步封装,因为中英文切换一般都是一次点击后,往后的统一每个页面都需要或不需要翻译,这就需要记录一个状态值,这里选用html5提供的storage来储存这个状态,并向外提供一个修改该状态并刷新页面的方法。故,该js(language.js)编写如下:")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[s("span",{pre:!0,attrs:{class:"token function"}},[t._v("$")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),s("span",{pre:!0,attrs:{class:"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("// do something ")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" script"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("document"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),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 string"}},[t._v('"script"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" \n script"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("type"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"text/javascript"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" \n script"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("src"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"http://www.microsoftTranslator.com/ajax/v3/WidgetV3.ashx?siteData=ueOIGRSKkd965FeEGM5JtQ**"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" \n document"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("getElementsByTagName")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'head'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),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 punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("appendChild")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("script"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" \n \n \n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" value "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" sessionStorage"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("getItem")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"language"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n document"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function-variable function"}},[t._v("onreadystatechange")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),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("document"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("readyState "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'complete'")]),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")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("===")]),s("span",{pre:!0,attrs:{class:"token string"}},[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 Microsoft"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Translator"),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 function"}},[t._v("Translate")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'zh-CHS'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),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(" onProgress"),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(" onComplete"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" onRestoreOriginal"),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 "),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 keyword"}},[t._v("function")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("onProgress")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("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 punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("onError")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("error")]),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 punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("onComplete")]),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("$")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"#WidgetFloaterPanels"')]),s("span",{pre:!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("hide")]),s("span",{pre:!0,attrs:{class:"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 keyword"}},[t._v("function")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("onRestoreOriginal")]),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 punctuation"}},[t._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("function")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("translate")]),s("span",{pre:!0,attrs:{class:"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("var")]),t._v(" value "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" sessionStorage"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("getItem")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"language"')]),s("span",{pre:!0,attrs:{class:"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")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("===")]),s("span",{pre:!0,attrs:{class:"token string"}},[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 sessionStorage"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("setItem")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"language"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"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("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 sessionStorage"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("setItem")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"language"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[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 window"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("location"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("reload")]),s("span",{pre:!0,attrs:{class:"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 punctuation"}},[t._v("}")]),t._v("\n")])])]),s("h3",{attrs:{id:"_2、编写测试页面"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_2、编写测试页面"}},[t._v("#")]),t._v(" 2、编写测试页面")]),t._v(" "),s("p",[t._v("编写一个测试页面(test.html)。要使用上面的language.js,必须进行以下三步:")]),t._v(" "),s("ol",[s("li",[t._v("设置页面编码为utf-8 "),s("meta",{attrs:{charset:"UTF-8"}})]),t._v(" "),s("li",[t._v("引入jquery和language.js")]),t._v(" "),s("li",[t._v("设置按钮的点击事件,去调用中英文切换函数:translate();")])]),t._v(" "),s("div",{staticClass:"language-html extra-class"},[s("pre",{pre:!0,attrs:{class:"language-html"}},[s("code",[s("span",{pre:!0,attrs:{class:"token doctype"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("head")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[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("Microsoft Widget API Sample"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("meta")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("charset")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("UTF-8"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("/>")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("script")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("type")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("text/javascript"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("src")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("jquery-1.11.3.js"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token script"}}),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("script")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("type")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("text/javascript"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("src")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("language.js"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token script"}}),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("body")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("button")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("id")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("change"),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 tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("div")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token special-attr"}},[s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("style")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),s("span",{pre:!0,attrs:{class:"token value css language-css"}},[s("span",{pre:!0,attrs:{class:"token property"}},[t._v("text-align")]),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(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n 你好\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("script")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("type")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("text/javascript"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token script"}},[s("span",{pre:!0,attrs:{class:"token language-javascript"}},[t._v("\n "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("$")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"#change"')]),s("span",{pre:!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("click")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),s("span",{pre:!0,attrs:{class:"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("translate")]),s("span",{pre:!0,attrs:{class:"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 tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])]),s("p",[t._v("试试看效果吧,反正我觉得还行~")]),t._v(" "),s("h2",{attrs:{id:"三、其他"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#三、其他"}},[t._v("#")]),t._v(" 三、其他")]),t._v(" "),s("p",[t._v("上面编写的language.js中写死了中文转英文(zh-CHS转en),如果项目需要其他语言的转换,对language.js进行自定义扩展即可。")])])}),[],!1,null,null,null);s.default=p.exports}}]); \ No newline at end of file diff --git a/assets/js/149.73f1b672.js b/assets/js/149.73f1b672.js new file mode 100644 index 000000000..2474ea78f --- /dev/null +++ b/assets/js/149.73f1b672.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[149],{462:function(t,a,s){"use strict";s.r(a);var n=s(8),e=Object(n.a)({},(function(){var t=this,a=t._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h2",{attrs:{id:"一、简介"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#一、简介"}},[t._v("#")]),t._v(" 一、简介")]),t._v(" "),a("p",[t._v("微信小程序播放教育类视频要求具备有相关资质,但这些资质一般公司很难短时间申请下来(甚至有的公司压根就申请不了),而【短视频播放器小程序插件】含有《信息网络传播视听节目许可证》的资质证书备案,可以利用该插件来解决资质问题,相关截图如下:")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202205152148376.png",alt:""}})]),t._v(" "),a("blockquote",[a("p",[t._v("图片来源:"),a("a",{attrs:{href:"https://cloud.tencent.com/document/product/266/36849",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://cloud.tencent.com/document/product/266/36849"),a("OutboundLink")],1)])]),t._v(" "),a("p",[t._v("采购流程于技术无关,以下内容着重讲解如何集成该微信小程序插件。")]),t._v(" "),a("blockquote",[a("p",[t._v("注:【短视频播放器小程序插件】授权费 3 万/年(有 14 天试用 Licence),如果有购买腾讯云其他服务的话,满足一定条件,会赠送 1 年 免费使用 Licence,详情找腾讯云客服咨询(2022 年 04 月如此,赠送情况可能随时会变)。")])]),t._v(" "),a("h2",{attrs:{id:"二、使用"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#二、使用"}},[t._v("#")]),t._v(" 二、使用")]),t._v(" "),a("ul",[a("li",[t._v("激活:在腾讯云控制台激活插件 Licence 之后,才能正常使用该播放器插件。")]),t._v(" "),a("li",[t._v("appid:后面插件用到的 appid 需要在【腾讯云控制台】>账号信息中查看获取。")]),t._v(" "),a("li",[t._v("云点播短视频播放器-开发文档:"),a("a",{attrs:{href:"https://mp.weixin.qq.com/wxopen/plugindevdoc?appid=wx116d0dd5e6a39ac7&lang=zh_CN",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://mp.weixin.qq.com/wxopen/plugindevdoc?appid=wx116d0dd5e6a39ac7&lang=zh_CN"),a("OutboundLink")],1)])]),t._v(" "),a("h3",{attrs:{id:"_1、绑定插件"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_1、绑定插件"}},[t._v("#")]),t._v(" 1、绑定插件")]),t._v(" "),a("p",[t._v("因为微信小程序插件没有实质代码或 SDK,所以无法在本地添加集成,需要在微信小程序平台,将 "),a("code",[t._v("小程序AppID")]),t._v(" 与 "),a("code",[t._v("插件AppID")]),t._v(" 进行绑定(即给小程序添加插件),开发者工具在编译时会自动引入,绑定有 2 种方式:")]),t._v(" "),a("ul",[a("li",[t._v("方式 1:登录微信小程序平台,在 "),a("code",[t._v("设置-第三方设置")]),t._v(" 中找到 "),a("code",[t._v("添加插件")]),t._v(",输入插件 AppID("),a("code",[t._v("wx116d0dd5e6a39ac7")]),t._v(" )搜索并添加:")])]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202205152216296.png",alt:""}})]),t._v(" "),a("ul",[a("li",[t._v("方式 2:在 "),a("a",{attrs:{href:"https://mp.weixin.qq.com/wxopen/pluginbasicprofile?action=intro&appid=wx116d0dd5e6a39ac7&lang=zh_CN",target:"_blank",rel:"noopener noreferrer"}},[t._v("云点播短视频播放器文档"),a("OutboundLink")],1),t._v(" 页面直接点击 "),a("code",[t._v("添加插件")]),t._v(":")])]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202205152218693.png",alt:""}})]),t._v(" "),a("blockquote",[a("p",[t._v("云点播短视频播放器文档:"),a("a",{attrs:{href:"https://mp.weixin.qq.com/wxopen/pluginbasicprofile?action=intro&appid=wx116d0dd5e6a39ac7&lang=zh_CN",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://mp.weixin.qq.com/wxopen/pluginbasicprofile?action=intro&appid=wx116d0dd5e6a39ac7&lang=zh_CN"),a("OutboundLink")],1)])]),t._v(" "),a("h3",{attrs:{id:"_2、集成插件"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_2、集成插件"}},[t._v("#")]),t._v(" 2、集成插件")]),t._v(" "),a("p",[t._v("微信小程序原生工程需要 "),a("code",[t._v("在 app.json 里声明使用的插件及版本")]),t._v(",对应到 uniapp 工程,则是在 "),a("code",[t._v("manifest.json")]),t._v(" 文件中微信小程序特有配置(即 "),a("code",[t._v("mp-weixin")]),t._v(" 节点) 处,进行 "),a("code",[t._v("plugins")]),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 comment"}},[t._v("// manifest.json 源码视图")]),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 property"}},[t._v('"mp-weixin"')]),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 property"}},[t._v('"appid"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"wxxxxxxxxxxx"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"plugins"')]),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 comment"}},[t._v("// 云点播短视频播放器")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 文档:https://mp.weixin.qq.com/wxopen/plugindevdoc?appid=wx116d0dd5e6a39ac7&token=1835838344&lang=zh_CN")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"cloudPlayer"')]),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 property"}},[t._v('"version"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"0.1.2"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"provider"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"wx116d0dd5e6a39ac7"')]),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("blockquote",[a("p",[t._v("manifest.json 配置项说明:"),a("a",{attrs:{href:"https://uniapp.dcloud.io/collocation/manifest.html#%E9%85%8D%E7%BD%AE%E9%A1%B9%E5%88%97%E8%A1%A8",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://uniapp.dcloud.io/collocation/manifest.html"),a("OutboundLink")],1)])]),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("在 页面的 xxxx.json 里声明")]),t._v(",对应到 uniapp 工程,则是在 "),a("code",[t._v("pages.json")]),t._v(" 文件中,在需要使用插件的 "),a("code",[t._v("页面的 style")]),t._v(" 的微信小程序特有配置(即 "),a("code",[t._v("mp-weixin")]),t._v(" 节点)处,进行 "),a("code",[t._v("usingComponents")]),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 comment"}},[t._v("// pages.json")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"pages"')]),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 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 property"}},[t._v('"path"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"pages/course/course"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"style"')]),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 property"}},[t._v('"navigationBarTitleText"')]),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 property"}},[t._v('"mp-weixin"')]),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 comment"}},[t._v("// 云点播短视频播放器")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"usingComponents"')]),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 property"}},[t._v('"cloud-player"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"plugin://cloudPlayer/player"')]),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("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"globalStyle"')]),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 ...\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("blockquote",[a("p",[t._v("pages.json 配置项说明:"),a("a",{attrs:{href:"https://uniapp.dcloud.io/collocation/pages.html#style",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://uniapp.dcloud.io/collocation/pages.html#style"),a("OutboundLink")],1)])]),t._v(" "),a("p",[t._v("声明完哪个页面需要使用插件播放器之后,就可以在那个页面的布局文件中使用插件播放器了:")]),t._v(" "),a("div",{staticClass:"language-vue extra-class"},[a("pre",{pre:!0,attrs:{class:"language-vue"}},[a("code",[t._v("// 在wxml里插入\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("cloud-player")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("appid")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("xxxxx"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("fileid")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("xxxxxxxx"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("playerid")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("myPlayerId"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])]),a("blockquote",[a("p",[t._v("注:微信小程序原生工程中,页面是 "),a("code",[t._v("wxml")]),t._v(" 文件,uniapp 工程中是 "),a("code",[t._v("vue")]),t._v(" 文件。另外,目前这种声明方式只对单个配置过的页面有效,也就是说,如果其他页面也需要使用插件播放器,还需要在其他页面的"),a("code",[t._v("style")]),t._v("中单独进行配置,这就很麻烦了,不过现在不用烦恼,后面会解决这个问题。")])]),t._v(" "),a("h3",{attrs:{id:"_4、组件内使用播放器"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_4、组件内使用播放器"}},[t._v("#")]),t._v(" 4、组件内使用播放器")]),t._v(" "),a("p",[t._v("为了功能复用,以及方便代码维护,实际开发中,往往会自定义组件,对常用的布局、功能进行封装。微信小程序原生工程可以在自定义组件的 json 文件中进行配置,跟页面相同的 "),a("code",[t._v("usingComponents")]),t._v(" 配置即可:")]),t._v(" "),a("p",[a("img",{attrs:{src:"https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource20220417121922/image/202205152254744.png",alt:""}})]),t._v(" "),a("blockquote",[a("p",[t._v("官方的 "),a("a",{attrs:{href:"https://mp.weixin.qq.com/wxopen/plugindevdoc?appid=wx116d0dd5e6a39ac7&lang=zh_CN",target:"_blank",rel:"noopener noreferrer"}},[t._v("云点播短视频播放器-开发文档"),a("OutboundLink")],1),t._v(" 只说明在了如何在网页中使用插件,但没有对组件中如何使用插件进行说明,很无语,希望后续官方能完善一下。另外,这是我发起工单询问之后,腾讯技术售后给我的 demo 工程中的代码,是否有效暂不确定 -_-!")])]),t._v(" "),a("p",[t._v("uniapp 遵循 vue 规范,想要在自定义组件要使用其他自定义组件,需要在 vue 文件中的 "),a("code",[t._v(" + + + + + + + + + + + + +
+ + + diff --git a/categories/index.html b/categories/index.html new file mode 100644 index 000000000..8f5e23930 --- /dev/null +++ b/categories/index.html @@ -0,0 +1,454 @@ + + + + + + 分类 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/img/favicon.ico b/img/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..628cdc7938dbc80aaacf4f579754defab78699b5 GIT binary patch literal 16958 zcmeHMd2CeG9xjUFiaV|e5RIr9mxytTN-zNeAyGs?A^H$~(gy_PvHQXzrF{q#1SztJ z28yCk5Ck5y6gq8zu0UIs?i32sc5J8Hw6k^kz2A3+JKPycDP{VX_mJCj=iGCC%lBLF zB_yPi{Oj8{ME>>&d8%JXNVkxXkf-F5rI5LDab1nR-bw!##xM{t5HJuh5HJuh5HJuh z5HJuh5V$S`nwy)UU4evJTMyiOwci>o&cPm{fzrVvv^~upxj1_-^3$*D`SadxcfxA5 z!{hOuYnJm|=Vfbfp{&fN?)5U));7ZFYVz@{t#!cV_Wll}uhHWC*I)HY7T!}Po6QNQ z(|b-&|61=!uc^t6{qcoJOgw|6q%%lJID_!;bR;GftKVx@=U{70zMn2Ucm6^x=FLq* zK|uw!{gI3F=WF3{yYSm@1sFUy7TdNLi>w|$ky294*t986Fmod#y$t^tlYswxohiTl zbs?7CHDc?5MfBeilZ%E%2MUWT@$bn;FluyylHHHNHU6L;&0;@ZUORf=a5iJ+tTZ%H zCb^tWm)Oju^jG-HUWfcHE3d~lVY07T_PE^mas3I*oSBAi7afDe(tzYc7kv6FKEf+4 zqZ}JH76>hm$nJtmt|?Q}6mPxglqFTL`gL<5DytgAN1RAXzJTw(J0|yTrT6SP8G=iL z{#d*3scU|r75Vv9IF)O~`0+_%*Gl9TRHLejetgNdX}kaJEr8ivhcNLI`XfhEQxgUc ziNlKTbKn-eYHPgb_>B4xgN&mU*chG9C3TK`r%o09FVavCpF&=qRXftI*=x@xu{C|H4(1EhICr)N6DK93^gR(n-g9KAC91fSr+zh8f z{C{Su;=$ZdQ)kDybJfazG!xHI->+w8V$xSh2>n+g_Qw?=;=f0+bm=j~?k~pj6>#SF^}^GwFr6(q zhd#J%TLDs1D%Ae=|MBv)h&{(oG-3PpJeZP7v1d=QYE=At{h#(qODjcHwGGo}q^LYY44XVIvAxU^Ulxfo`V|NB0r65cmuK`t z)=pDm2|`1Y5f@*CHEVOQZ|_;PKNx?g->K78m^LF7*(WU6x4%T$Qu9gjP#DNQ%E{cz z_^0hD|FmgoXtX=9KfV;KbLyPzRp-QKSVO*DoC$Mzt>kc*aUJUNevKa_Mb1Y`~#IhtSx_SZ%=p z+6e+_ef;sqkpI)cd+)s$RN9(I4u=EJJo5~0z4cb~=+Q&^A&2(psx#%=x^*kwe*0~_ z^wLWhJ9aGQ&z~>vht?Kl<$lJxTY7n?)7j!4jVbq@IB^0GJ@k;$pLDqv%t3mSCr`#r zH{GOo5a%5`cC?8F&n{iM6rDPC@}1@RTW+}p{rdI8qD6~TzSoFx|L)zp@yjp2;E_ij z!ABo`)T%z*KWNY(MVmYpFJ9d0evQwy%;eF(e}6yu83Sz@t5&VLOn2(f>$TTjQ#icy z&O4~6s8Di~m&s(p!i5V}JTu->Q&X?Vim^0m)F?&gi6@@Gj2SaJME_~irupf=Wy_W; z%A|kr$}6wrKmtV&7&p$8kO6QgABSwty!|aPMzR;8O-4mgX6AQ*;cz8J0u3and&ExUhbI++9LRmFhwDJA-->-5* zLqmg#HSHPa?%lh$SaYc7x^?UD;fEjkXnp?q=N+Oy8GFCq`&IZ0{XV1 zq9V+iH4As%d8dz_rZ3m?^78z!rjP8}wM*%xw=M6pva)c`J@@$NOrJizL-bEcNm2P* zlXK3TIc?JaN}DJ3jJN+K!f{xIi-|rrL@IgObdcM4W z_0?Cw{L|8Vil#4T(xQ)32fdE$k8C!ZG9C9ZZwB@afjk)(`n44+Rw(_0jREPgZ`1P% zyr-;;Mf#;Sc#cYId70!XVgYvMB&=(ILJg6rXlzU>U?*RjCLtDJ@#v7`y)fmzjSo?S`a6jd}?Y7$x z78cfGpG({(PMoNGq-)o%3Io31FcxEDWAXUokNaUr`pk!XcKrBpO@yG$)TK|KK8h!K zvG!zUX3DqQz3QDPGBQ&2pL+ZM`RAXP())aQE&6r@hi#9_4xRBMT36B^KZWSrizyb9(cef3!m-Wx$~;>v=`|%`S;{Nc%4=Z0}f6sbM|D=2eB?nWNoSYoL9*yUyJAH}u;P%^ZS2cm} zy2OR=e|ijv4SPSmzC5G<{q)mM`<)3k4($K*{Pg#%PwYvUduPv{t!BpkyYIg1^Y56L z7!|+7OMjMmaO%{l>fMOzj0@Juvu7`TyC)Xe+1cpZx3Aw>Jss9&J!ZkypYLV-_M_+7 z(mnmOyS%T_Cs^A~o;;~Up=``$%+LJx(b8G25lh;VHrEEv(zjbXw|@Ql4%MGB(1wBU zC4qXd|6q;Mdiv<-XiW_t4#~;Ms+MbK=@aY^XnV#3Z5?P!{-!@^XY@1A^E*IC56WP% zSil~g??UV|7A#nR*I$2K>CE0IG&EH8wftVdcM;bAz(G3v*1_*SyvB_ihu?qy9fuDe zR(c2O&huR39=j`n-w?DnV1oG?X-;|BLD(!YHgYm$tt%Er@EiDb{>FJm>X_E3q zej8(KwuD(*=^FbC1PlZW1PlZW1PlZW1PlZW1PlZW1PlZW1pY1v@QbVeSJ%Ib=HNFa JWBiQ~_&>R^!ZQE> literal 0 HcmV?d00001 diff --git a/index.html b/index.html new file mode 100644 index 000000000..799bcb601 --- /dev/null +++ b/index.html @@ -0,0 +1,115 @@ + + + + + + FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/js/global.js b/js/global.js new file mode 100644 index 000000000..03fb50b5e --- /dev/null +++ b/js/global.js @@ -0,0 +1,7 @@ +window.onload = function () { + var footer = document.getElementsByClassName("footer")[0] + console.log(footer.childNodes) + footer.childNodes[1].nodeValue = '微信公众号 ' + footer.childNodes[2].href = 'https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/20210131111432.png' + footer.childNodes[2].innerHTML = 'FullStackAction' +} \ No newline at end of file diff --git a/mobile/index.html b/mobile/index.html new file mode 100644 index 000000000..26551d49b --- /dev/null +++ b/mobile/index.html @@ -0,0 +1,283 @@ + + + + + + 移动端 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/more/index.html b/more/index.html new file mode 100644 index 000000000..545e8c330 --- /dev/null +++ b/more/index.html @@ -0,0 +1,98 @@ + + + + + + 更多 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/note/designpattern/index.html b/note/designpattern/index.html new file mode 100644 index 000000000..b264a4861 --- /dev/null +++ b/note/designpattern/index.html @@ -0,0 +1,106 @@ + + + + + + 深入浅出设计模式Java版 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/note/flutter/index.html b/note/flutter/index.html new file mode 100644 index 000000000..e73627125 --- /dev/null +++ b/note/flutter/index.html @@ -0,0 +1,92 @@ + + + + + + Flutter从入门到实战 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/note/flutter/lxf/review/index.html b/note/flutter/lxf/review/index.html new file mode 100644 index 000000000..2066b36c9 --- /dev/null +++ b/note/flutter/lxf/review/index.html @@ -0,0 +1,87 @@ + + + + + + Flutter复习 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/note/kotlin/index.html b/note/kotlin/index.html new file mode 100644 index 000000000..fafe4fb3c --- /dev/null +++ b/note/kotlin/index.html @@ -0,0 +1,118 @@ + + + + + + Kotlin快速入门进阶 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/00e6eb/index.html b/pages/00e6eb/index.html new file mode 100644 index 000000000..187066c60 --- /dev/null +++ b/pages/00e6eb/index.html @@ -0,0 +1,110 @@ + + + + + + 解决WebView内存泄漏 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/022231/index.html b/pages/022231/index.html new file mode 100644 index 000000000..32f7a5e80 --- /dev/null +++ b/pages/022231/index.html @@ -0,0 +1,141 @@ + + + + + + Kotlin - 区间与数组 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/030ff5/index.html b/pages/030ff5/index.html new file mode 100644 index 000000000..0e9ba7f42 --- /dev/null +++ b/pages/030ff5/index.html @@ -0,0 +1,398 @@ + + + + + + 热修复 - Bugly让热修复变得如此简单 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/0422cb/index.html b/pages/0422cb/index.html new file mode 100644 index 000000000..229504fe9 --- /dev/null +++ b/pages/0422cb/index.html @@ -0,0 +1,160 @@ + + + + + + Flutter - 获取ListView当前正在显示的Widget信息 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/04a87c/index.html b/pages/04a87c/index.html new file mode 100644 index 000000000..117d598cf --- /dev/null +++ b/pages/04a87c/index.html @@ -0,0 +1,281 @@ + + + + + + Gradle入门系列(三) - 初识Gradle与Project | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/05278d/index.html b/pages/05278d/index.html new file mode 100644 index 000000000..75984efff --- /dev/null +++ b/pages/05278d/index.html @@ -0,0 +1,163 @@ + + + + + + Docker - Dockerfile的使用 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/0790a0/index.html b/pages/0790a0/index.html new file mode 100644 index 000000000..1b5d83754 --- /dev/null +++ b/pages/0790a0/index.html @@ -0,0 +1,170 @@ + + + + + + Kotlin - 尾递归优化 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/07f268/index.html b/pages/07f268/index.html new file mode 100644 index 000000000..20a3eee0f --- /dev/null +++ b/pages/07f268/index.html @@ -0,0 +1,289 @@ + + + + + + Android - 云游戏本地悬浮输入框实现 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/084bc4/index.html b/pages/084bc4/index.html new file mode 100644 index 000000000..54c3e4030 --- /dev/null +++ b/pages/084bc4/index.html @@ -0,0 +1,252 @@ + + + + + + Swift - 掌控Moya的网络请求、数据解析与缓存 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/084bf9/index.html b/pages/084bf9/index.html new file mode 100644 index 000000000..904a4550f --- /dev/null +++ b/pages/084bf9/index.html @@ -0,0 +1,127 @@ + + + + + + Docker - 操作镜像资源 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/087ea3/index.html b/pages/087ea3/index.html new file mode 100644 index 000000000..5e112d4aa --- /dev/null +++ b/pages/087ea3/index.html @@ -0,0 +1,558 @@ + + + + + + uniapp - 腾讯云点播小程序插件 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/0909a3/index.html b/pages/0909a3/index.html new file mode 100644 index 000000000..39e5cd5a9 --- /dev/null +++ b/pages/0909a3/index.html @@ -0,0 +1,142 @@ + + + + + + iOS - Swift仿微信聊天图片显示 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/091864/index.html b/pages/091864/index.html new file mode 100644 index 000000000..6d72bc0f4 --- /dev/null +++ b/pages/091864/index.html @@ -0,0 +1,252 @@ + + + + + + DesignPattern - 享元模式【结构型】 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/0c1879/index.html b/pages/0c1879/index.html new file mode 100644 index 000000000..a8b65e795 --- /dev/null +++ b/pages/0c1879/index.html @@ -0,0 +1,278 @@ + + + + + + AndroidTV - 获取Mac地址 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/0d47a0/index.html b/pages/0d47a0/index.html new file mode 100644 index 000000000..dbb1d2f08 --- /dev/null +++ b/pages/0d47a0/index.html @@ -0,0 +1,233 @@ + + + + + + LayoutInflater源码分析与应用 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/0d4d47/index.html b/pages/0d4d47/index.html new file mode 100644 index 000000000..41346a09b --- /dev/null +++ b/pages/0d4d47/index.html @@ -0,0 +1,131 @@ + + + + + + iOS - Cocoapods-创建第三方框架 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/0e461d/index.html b/pages/0e461d/index.html new file mode 100644 index 000000000..98ef5e078 --- /dev/null +++ b/pages/0e461d/index.html @@ -0,0 +1,143 @@ + + + + + + 这可能是目前最方便的网站中英文切换(理论支持所有语言) | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/0ee6a4/index.html b/pages/0ee6a4/index.html new file mode 100644 index 000000000..b0482a937 --- /dev/null +++ b/pages/0ee6a4/index.html @@ -0,0 +1,167 @@ + + + + + + Flutter - 解决Connection closed before full header was received | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/0f2500/index.html b/pages/0f2500/index.html new file mode 100644 index 000000000..67ab5a161 --- /dev/null +++ b/pages/0f2500/index.html @@ -0,0 +1,352 @@ + + + + + + MaterialDesign之对TabLayout的探索 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/0f3155/index.html b/pages/0f3155/index.html new file mode 100644 index 000000000..40b85a067 --- /dev/null +++ b/pages/0f3155/index.html @@ -0,0 +1,501 @@ + + + + + + Flutter - 基础Widget | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/0fcdc8/index.html b/pages/0fcdc8/index.html new file mode 100644 index 000000000..5b378164a --- /dev/null +++ b/pages/0fcdc8/index.html @@ -0,0 +1,245 @@ + + + + + + Flutter - Dart事件循环机制与异步 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/10277d/index.html b/pages/10277d/index.html new file mode 100644 index 000000000..996d0912a --- /dev/null +++ b/pages/10277d/index.html @@ -0,0 +1,349 @@ + + + + + + Python - 爬虫之Selenium | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/1084c4/index.html b/pages/1084c4/index.html new file mode 100644 index 000000000..169dd94dc --- /dev/null +++ b/pages/1084c4/index.html @@ -0,0 +1,106 @@ + + + + + + 热修复与插件化基础 - Java与Android虚拟机 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/117e5d/index.html b/pages/117e5d/index.html new file mode 100644 index 000000000..0ad032326 --- /dev/null +++ b/pages/117e5d/index.html @@ -0,0 +1,148 @@ + + + + + + Kotlin - 函数式编程 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/1195b1/index.html b/pages/1195b1/index.html new file mode 100644 index 000000000..d98d0c7c8 --- /dev/null +++ b/pages/1195b1/index.html @@ -0,0 +1,294 @@ + + + + + + RePluginX - 兼容AndroidX并加入新特性开发纪要 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/13365e/index.html b/pages/13365e/index.html new file mode 100644 index 000000000..5106333ef --- /dev/null +++ b/pages/13365e/index.html @@ -0,0 +1,202 @@ + + + + + + Android - OkHttp 访问 https 的怪问题 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/13bf45/index.html b/pages/13bf45/index.html new file mode 100644 index 000000000..f45773df2 --- /dev/null +++ b/pages/13bf45/index.html @@ -0,0 +1,270 @@ + + + + + + LinkedList与Queue源码分析 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/13fb2b/index.html b/pages/13fb2b/index.html new file mode 100644 index 000000000..8c8b05b72 --- /dev/null +++ b/pages/13fb2b/index.html @@ -0,0 +1,151 @@ + + + + + + Kotlin - 高阶函数与函数引用 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/15960b/index.html b/pages/15960b/index.html new file mode 100644 index 000000000..177c5428e --- /dev/null +++ b/pages/15960b/index.html @@ -0,0 +1,148 @@ + + + + + + Mac远程登录到iOS设备 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/15d73d/index.html b/pages/15d73d/index.html new file mode 100644 index 000000000..144fa84e2 --- /dev/null +++ b/pages/15d73d/index.html @@ -0,0 +1,377 @@ + + + + + + Flutter - 瀑布流交替播放视频 🎞 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/1751fb/index.html b/pages/1751fb/index.html new file mode 100644 index 000000000..20119c982 --- /dev/null +++ b/pages/1751fb/index.html @@ -0,0 +1,192 @@ + + + + + + Android - 混淆 java-library 工程 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/17d6bb/index.html b/pages/17d6bb/index.html new file mode 100644 index 000000000..1658bcb80 --- /dev/null +++ b/pages/17d6bb/index.html @@ -0,0 +1,139 @@ + + + + + + iOS - 通过runtime获取某个类中所有的变量和方法 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/19fce1/index.html b/pages/19fce1/index.html new file mode 100644 index 000000000..30a037ad1 --- /dev/null +++ b/pages/19fce1/index.html @@ -0,0 +1,452 @@ + + + + + + AppRTC实战 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/1af1e6/index.html b/pages/1af1e6/index.html new file mode 100644 index 000000000..e783c8414 --- /dev/null +++ b/pages/1af1e6/index.html @@ -0,0 +1,203 @@ + + + + + + Flutter - 实现聊天键盘与功能面板的丝滑切换 🍻 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/1cb0c1/index.html b/pages/1cb0c1/index.html new file mode 100644 index 000000000..293daf70b --- /dev/null +++ b/pages/1cb0c1/index.html @@ -0,0 +1,116 @@ + + + + + + iOS - Swift UITableView的scrollToRow的坑 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/1d7592/index.html b/pages/1d7592/index.html new file mode 100644 index 000000000..bd64d2250 --- /dev/null +++ b/pages/1d7592/index.html @@ -0,0 +1,156 @@ + + + + + + Kotlin - 面向对象之抽象类与接口 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/2069ac/index.html b/pages/2069ac/index.html new file mode 100644 index 000000000..3c2af7f2d --- /dev/null +++ b/pages/2069ac/index.html @@ -0,0 +1,158 @@ + + + + + + iOS - 揭露Block的内部实现原理 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/21e20f/index.html b/pages/21e20f/index.html new file mode 100644 index 000000000..6a7229779 --- /dev/null +++ b/pages/21e20f/index.html @@ -0,0 +1,428 @@ + + + + + + AndroidNDK - Cmake详解 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/23e976/index.html b/pages/23e976/index.html new file mode 100644 index 000000000..b377f23d7 --- /dev/null +++ b/pages/23e976/index.html @@ -0,0 +1,220 @@ + + + + + + AI - 发现一个超级好用的 AI 聚合平台 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/242c2b/index.html b/pages/242c2b/index.html new file mode 100644 index 000000000..3068caf15 --- /dev/null +++ b/pages/242c2b/index.html @@ -0,0 +1,127 @@ + + + + + + iOS逆向 - 应用脱壳 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/253dac/index.html b/pages/253dac/index.html new file mode 100644 index 000000000..132a9fa69 --- /dev/null +++ b/pages/253dac/index.html @@ -0,0 +1,203 @@ + + + + + + ReactorKit + RxDataSources列表多次刷新的解决方案 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/26b199/index.html b/pages/26b199/index.html new file mode 100644 index 000000000..3ff06f56a --- /dev/null +++ b/pages/26b199/index.html @@ -0,0 +1,303 @@ + + + + + + Flutter - IM保持消息位置大升级(支持ChatGPT生成式消息) 🤖 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/29287b/index.html b/pages/29287b/index.html new file mode 100644 index 000000000..8788f0a34 --- /dev/null +++ b/pages/29287b/index.html @@ -0,0 +1,193 @@ + + + + + + DesignPattern - 模板方法模式【行为型】 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/294865/index.html b/pages/294865/index.html new file mode 100644 index 000000000..2d362f238 --- /dev/null +++ b/pages/294865/index.html @@ -0,0 +1,277 @@ + + + + + + Android面向切面编程(AOP) | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/2a27e8/index.html b/pages/2a27e8/index.html new file mode 100644 index 000000000..e939ff299 --- /dev/null +++ b/pages/2a27e8/index.html @@ -0,0 +1,197 @@ + + + + + + Material Design 兼容性控件学习 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/2afdff/index.html b/pages/2afdff/index.html new file mode 100644 index 000000000..e3e17ec6b --- /dev/null +++ b/pages/2afdff/index.html @@ -0,0 +1,137 @@ + + + + + + Flutter - 轻松搞定屏幕旋转功能 😎 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/2c5ae4/index.html b/pages/2c5ae4/index.html new file mode 100644 index 000000000..1876305ce --- /dev/null +++ b/pages/2c5ae4/index.html @@ -0,0 +1,548 @@ + + + + + + Flutter - 基础布局 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/2ed5f7/index.html b/pages/2ed5f7/index.html new file mode 100644 index 000000000..b60900921 --- /dev/null +++ b/pages/2ed5f7/index.html @@ -0,0 +1,175 @@ + + + + + + DesignPattern - 桥接模式【结构型】 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/2ffb7f/index.html b/pages/2ffb7f/index.html new file mode 100644 index 000000000..09d17712a --- /dev/null +++ b/pages/2ffb7f/index.html @@ -0,0 +1,116 @@ + + + + + + Docker - 安装、加速和基本使用 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/326c98/index.html b/pages/326c98/index.html new file mode 100644 index 000000000..a22046700 --- /dev/null +++ b/pages/326c98/index.html @@ -0,0 +1,199 @@ + + + + + + Flutter - 滚动视图中的表单防遮挡 🗒 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/328069/index.html b/pages/328069/index.html new file mode 100644 index 000000000..19a525a30 --- /dev/null +++ b/pages/328069/index.html @@ -0,0 +1,124 @@ + + + + + + Android - 自动系统签名 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/32a06e/index.html b/pages/32a06e/index.html new file mode 100644 index 000000000..f656515f8 --- /dev/null +++ b/pages/32a06e/index.html @@ -0,0 +1,142 @@ + + + + + + FQ-各平台终端设置代理的方法 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/334ef0/index.html b/pages/334ef0/index.html new file mode 100644 index 000000000..7bd551963 --- /dev/null +++ b/pages/334ef0/index.html @@ -0,0 +1,132 @@ + + + + + + Kotlin - 方法重载与默认参数 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/337d0f/index.html b/pages/337d0f/index.html new file mode 100644 index 000000000..e7bf64147 --- /dev/null +++ b/pages/337d0f/index.html @@ -0,0 +1,106 @@ + + + + + + Docker - 网络管理 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/3420a3/index.html b/pages/3420a3/index.html new file mode 100644 index 000000000..27d6255b0 --- /dev/null +++ b/pages/3420a3/index.html @@ -0,0 +1,148 @@ + + + + + + Kotlin - 内部类 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/34334d/index.html b/pages/34334d/index.html new file mode 100644 index 000000000..f98e3733a --- /dev/null +++ b/pages/34334d/index.html @@ -0,0 +1,156 @@ + + + + + + DesignPattern - 状态模式【行为型】 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/34e11b/index.html b/pages/34e11b/index.html new file mode 100644 index 000000000..c240ec60f --- /dev/null +++ b/pages/34e11b/index.html @@ -0,0 +1,121 @@ + + + + + + FQ - Terminal下的代理工具ProxyChains-NG | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/37adf1/index.html b/pages/37adf1/index.html new file mode 100644 index 000000000..e25093422 --- /dev/null +++ b/pages/37adf1/index.html @@ -0,0 +1,131 @@ + + + + + + Kotlin - 运算符与中缀表达式 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/39525c/index.html b/pages/39525c/index.html new file mode 100644 index 000000000..800a956ec --- /dev/null +++ b/pages/39525c/index.html @@ -0,0 +1,221 @@ + + + + + + Kotlin - 属性代理 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/398f01/index.html b/pages/398f01/index.html new file mode 100644 index 000000000..d69eabfd9 --- /dev/null +++ b/pages/398f01/index.html @@ -0,0 +1,183 @@ + + + + + + iOS - 面向协议方式封装空白页功能 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/399558/index.html b/pages/399558/index.html new file mode 100644 index 000000000..a6d86ec6e --- /dev/null +++ b/pages/399558/index.html @@ -0,0 +1,91 @@ + + + + + + RePlugin集成Fresco | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/3a12b8/index.html b/pages/3a12b8/index.html new file mode 100644 index 000000000..391051090 --- /dev/null +++ b/pages/3a12b8/index.html @@ -0,0 +1,275 @@ + + + + + + uniapp - tensorflowjs 之小程序环境集成 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/3b59d7/index.html b/pages/3b59d7/index.html new file mode 100644 index 000000000..da7d2a57f --- /dev/null +++ b/pages/3b59d7/index.html @@ -0,0 +1,227 @@ + + + + + + Kotlin - 改良设计模式 - 责任链模式 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/41ce5a/index.html b/pages/41ce5a/index.html new file mode 100644 index 000000000..48b839a3b --- /dev/null +++ b/pages/41ce5a/index.html @@ -0,0 +1,190 @@ + + + + + + Kotlin - 改良设计模式 - 装饰者模式 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/42026f/index.html b/pages/42026f/index.html new file mode 100644 index 000000000..9a6bbe6fe --- /dev/null +++ b/pages/42026f/index.html @@ -0,0 +1,184 @@ + + + + + + RxSwift + MJRefresh 打造自动处理刷新控件状态 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/43645f/index.html b/pages/43645f/index.html new file mode 100644 index 000000000..d83596bd0 --- /dev/null +++ b/pages/43645f/index.html @@ -0,0 +1,90 @@ + + + + + + 一天内加入 Flutter 和 FlutterCandies 两大组织是什么体验 🧐 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/443e18/index.html b/pages/443e18/index.html new file mode 100644 index 000000000..9a1556833 --- /dev/null +++ b/pages/443e18/index.html @@ -0,0 +1,118 @@ + + + + + + iOS - 实现UINavigation全屏滑动返回(一) | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/4517cb/index.html b/pages/4517cb/index.html new file mode 100644 index 000000000..fc284f74f --- /dev/null +++ b/pages/4517cb/index.html @@ -0,0 +1,855 @@ + + + + + + Flutter - 探索run命令到底做了什么 🤔 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/46b6cc/index.html b/pages/46b6cc/index.html new file mode 100644 index 000000000..f9a4b8b0e --- /dev/null +++ b/pages/46b6cc/index.html @@ -0,0 +1,220 @@ + + + + + + iOS-组件化开发(四):fastlane实现pod自动化 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/4784fc/index.html b/pages/4784fc/index.html new file mode 100644 index 000000000..46e5d99ad --- /dev/null +++ b/pages/4784fc/index.html @@ -0,0 +1,154 @@ + + + + + + Flutter - 热更新 Shorebird 1.0 正式版来了 🐦 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/48f6d3/index.html b/pages/48f6d3/index.html new file mode 100644 index 000000000..cf31e74c7 --- /dev/null +++ b/pages/48f6d3/index.html @@ -0,0 +1,157 @@ + + + + + + Ubuntu安装nginx来搭建推流服务器 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/4b18ae/index.html b/pages/4b18ae/index.html new file mode 100644 index 000000000..30111c7eb --- /dev/null +++ b/pages/4b18ae/index.html @@ -0,0 +1,219 @@ + + + + + + Flutter - 混合开发 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/4bbcbf/index.html b/pages/4bbcbf/index.html new file mode 100644 index 000000000..35e3bd3fb --- /dev/null +++ b/pages/4bbcbf/index.html @@ -0,0 +1,385 @@ + + + + + + Flutter - 如何快速搓一个微信通讯录列表(azlist) 📓 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/4c1e21/index.html b/pages/4c1e21/index.html new file mode 100644 index 000000000..a0bebcfcd --- /dev/null +++ b/pages/4c1e21/index.html @@ -0,0 +1,93 @@ + + + + + + AI - 免费搭建一个私有的 ChatGPT | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/4d2b48/index.html b/pages/4d2b48/index.html new file mode 100644 index 000000000..4a6390aad --- /dev/null +++ b/pages/4d2b48/index.html @@ -0,0 +1,472 @@ + + + + + + Flutter - StateWidget与生命周期 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/4e6b65/index.html b/pages/4e6b65/index.html new file mode 100644 index 000000000..5abe6a282 --- /dev/null +++ b/pages/4e6b65/index.html @@ -0,0 +1,165 @@ + + + + + + RePlugin强制退出App | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/526c58/index.html b/pages/526c58/index.html new file mode 100644 index 000000000..67781147f --- /dev/null +++ b/pages/526c58/index.html @@ -0,0 +1,405 @@ + + + + + + DroidPlugin手札 - home键强杀处理 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/531d2b/index.html b/pages/531d2b/index.html new file mode 100644 index 000000000..e8239a917 --- /dev/null +++ b/pages/531d2b/index.html @@ -0,0 +1,300 @@ + + + + + + Flutter - 实现列表上下拉切换header | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/541c9c/index.html b/pages/541c9c/index.html new file mode 100644 index 000000000..824701f2a --- /dev/null +++ b/pages/541c9c/index.html @@ -0,0 +1,817 @@ + + + + + + Dart的基础语法 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/56dd3d/index.html b/pages/56dd3d/index.html new file mode 100644 index 000000000..695c9b00a --- /dev/null +++ b/pages/56dd3d/index.html @@ -0,0 +1,234 @@ + + + + + + iOS - 打造Moya便捷解析库,提供RxSwift拓展 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/577e7c/index.html b/pages/577e7c/index.html new file mode 100644 index 000000000..5e10b67ea --- /dev/null +++ b/pages/577e7c/index.html @@ -0,0 +1,224 @@ + + + + + + iOS逆向 - 运行时分析(二)Cycript | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/58ae6a/index.html b/pages/58ae6a/index.html new file mode 100644 index 000000000..9894a38e2 --- /dev/null +++ b/pages/58ae6a/index.html @@ -0,0 +1,276 @@ + + + + + + DesignPattern - 适配器模式【结构型】 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/5944f7/index.html b/pages/5944f7/index.html new file mode 100644 index 000000000..b89bba873 --- /dev/null +++ b/pages/5944f7/index.html @@ -0,0 +1,183 @@ + + + + + + AndroidTV - 解决EditText焦点无法转移问题 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/5a5158/index.html b/pages/5a5158/index.html new file mode 100644 index 000000000..9cb9d725c --- /dev/null +++ b/pages/5a5158/index.html @@ -0,0 +1,128 @@ + + + + + + Flutter - 低版本在iOS14上遇到的问题与解决方案 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/5c4b50/index.html b/pages/5c4b50/index.html new file mode 100644 index 000000000..58449db9c --- /dev/null +++ b/pages/5c4b50/index.html @@ -0,0 +1,375 @@ + + + + + + Flutter - 我给官方提PR,解决run命令卡住问题 😃 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/5c8459/index.html b/pages/5c8459/index.html new file mode 100644 index 000000000..bc7bd6ea3 --- /dev/null +++ b/pages/5c8459/index.html @@ -0,0 +1,410 @@ + + + + + + RecyclerView之ItemDecoration | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/5f806d/index.html b/pages/5f806d/index.html new file mode 100644 index 000000000..b010019f2 --- /dev/null +++ b/pages/5f806d/index.html @@ -0,0 +1,90 @@ + + + + + + 性能优化 - 内存泄漏(2)工具分析篇 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/5f9bf6/index.html b/pages/5f9bf6/index.html new file mode 100644 index 000000000..6ca50a412 --- /dev/null +++ b/pages/5f9bf6/index.html @@ -0,0 +1,187 @@ + + + + + + Flutter - 船新升级😱支持观察第三方构建的滚动视图💪 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/614119/index.html b/pages/614119/index.html new file mode 100644 index 000000000..0360a52e4 --- /dev/null +++ b/pages/614119/index.html @@ -0,0 +1,266 @@ + + + + + + DesignPattern - 命令模式【行为型】 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/614da2/index.html b/pages/614da2/index.html new file mode 100644 index 000000000..8f3702450 --- /dev/null +++ b/pages/614da2/index.html @@ -0,0 +1,333 @@ + + + + + + Flutter - 引擎调试bug到提交PR实战 🐞 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/61cc74/index.html b/pages/61cc74/index.html new file mode 100644 index 000000000..f432c718e --- /dev/null +++ b/pages/61cc74/index.html @@ -0,0 +1,272 @@ + + + + + + Dart - 抽象类的实例化 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/61d6a8/index.html b/pages/61d6a8/index.html new file mode 100644 index 000000000..e5eeeb169 --- /dev/null +++ b/pages/61d6a8/index.html @@ -0,0 +1,218 @@ + + + + + + RecyclerView遇到notifyDataSetChanged无效时的解决方案 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/63c550/index.html b/pages/63c550/index.html new file mode 100644 index 000000000..685bc7c3e --- /dev/null +++ b/pages/63c550/index.html @@ -0,0 +1,175 @@ + + + + + + DesignPattern - 原型模式【创建型】 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/650506/index.html b/pages/650506/index.html new file mode 100644 index 000000000..f8b2d1641 --- /dev/null +++ b/pages/650506/index.html @@ -0,0 +1,804 @@ + + + + + + Flutter - Dart特性语法 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/661407/index.html b/pages/661407/index.html new file mode 100644 index 000000000..25800e123 --- /dev/null +++ b/pages/661407/index.html @@ -0,0 +1,112 @@ + + + + + + Kotlin - 常量与变量 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/67358d/index.html b/pages/67358d/index.html new file mode 100644 index 000000000..87599d196 --- /dev/null +++ b/pages/67358d/index.html @@ -0,0 +1,172 @@ + + + + + + DesignPattern - 观察者模式【行为型】 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/683c07/index.html b/pages/683c07/index.html new file mode 100644 index 000000000..176bd69b0 --- /dev/null +++ b/pages/683c07/index.html @@ -0,0 +1,152 @@ + + + + + + Flutter - 解决返回原生页面时dispose方法未被触发的问题 🐞 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/688a6b/index.html b/pages/688a6b/index.html new file mode 100644 index 000000000..b8fa26445 --- /dev/null +++ b/pages/688a6b/index.html @@ -0,0 +1,275 @@ + + + + + + 使用注解打造自己的IOC框架 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/6caa6f/index.html b/pages/6caa6f/index.html new file mode 100644 index 000000000..2be56e469 --- /dev/null +++ b/pages/6caa6f/index.html @@ -0,0 +1,181 @@ + + + + + + Flutter - 引擎调试(iOS篇)🛠 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/6d35fc/index.html b/pages/6d35fc/index.html new file mode 100644 index 000000000..3d345dafe --- /dev/null +++ b/pages/6d35fc/index.html @@ -0,0 +1,331 @@ + + + + + + 热修复 - Tinker多渠道加固配置 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/6f1cc2/index.html b/pages/6f1cc2/index.html new file mode 100644 index 000000000..0ab24fc9e --- /dev/null +++ b/pages/6f1cc2/index.html @@ -0,0 +1,144 @@ + + + + + + AndroidNDK - 编译 openssl | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/70f890/index.html b/pages/70f890/index.html new file mode 100644 index 000000000..c4586d058 --- /dev/null +++ b/pages/70f890/index.html @@ -0,0 +1,92 @@ + + + + + + DesignPattern - 设计原则与设计模式初探 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/7776d6/index.html b/pages/7776d6/index.html new file mode 100644 index 000000000..260789181 --- /dev/null +++ b/pages/7776d6/index.html @@ -0,0 +1,382 @@ + + + + + + 多RecyclerView同步滚动 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/78c482/index.html b/pages/78c482/index.html new file mode 100644 index 000000000..d7938551c --- /dev/null +++ b/pages/78c482/index.html @@ -0,0 +1,191 @@ + + + + + + Kotlin - 改良设计模式 - 迭代器模式 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/79a470/index.html b/pages/79a470/index.html new file mode 100644 index 000000000..f984a4da8 --- /dev/null +++ b/pages/79a470/index.html @@ -0,0 +1,91 @@ + + + + + + iOS - Swift3.0 高仿喜马拉雅FM | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/79e15e/index.html b/pages/79e15e/index.html new file mode 100644 index 000000000..9e7d28f15 --- /dev/null +++ b/pages/79e15e/index.html @@ -0,0 +1,294 @@ + + + + + + RecyclerView之使用ItemTouchHelper实现交互动画 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/79e7e5/index.html b/pages/79e7e5/index.html new file mode 100644 index 000000000..97cd4a5ee --- /dev/null +++ b/pages/79e7e5/index.html @@ -0,0 +1,311 @@ + + + + + + Android - setVisibility() 失效,竟然是因为内存泄露 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/79efd2/index.html b/pages/79efd2/index.html new file mode 100644 index 000000000..986a02c19 --- /dev/null +++ b/pages/79efd2/index.html @@ -0,0 +1,213 @@ + + + + + + vue - Vue2兼容低版本浏览器 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/7b4815/index.html b/pages/7b4815/index.html new file mode 100644 index 000000000..16be73040 --- /dev/null +++ b/pages/7b4815/index.html @@ -0,0 +1,112 @@ + + + + + + Kotlin - 类及成员的可见性 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/7b7220/index.html b/pages/7b7220/index.html new file mode 100644 index 000000000..5039556a0 --- /dev/null +++ b/pages/7b7220/index.html @@ -0,0 +1,123 @@ + + + + + + iOS - 给高仿微信添加直播聊天功能 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/7b86ec/index.html b/pages/7b86ec/index.html new file mode 100644 index 000000000..fae09a726 --- /dev/null +++ b/pages/7b86ec/index.html @@ -0,0 +1,227 @@ + + + + + + Flutter - 解决原生弹窗的触摸事件被Flutter响应的问题 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/7bacc4/index.html b/pages/7bacc4/index.html new file mode 100644 index 000000000..1e0d7fbbd --- /dev/null +++ b/pages/7bacc4/index.html @@ -0,0 +1,116 @@ + + + + + + iOS-组件化开发(三):加载资源文件 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/7cd064/index.html b/pages/7cd064/index.html new file mode 100644 index 000000000..87194e98f --- /dev/null +++ b/pages/7cd064/index.html @@ -0,0 +1,148 @@ + + + + + + Kotlin - 参数与异常 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/7e1f42/index.html b/pages/7e1f42/index.html new file mode 100644 index 000000000..38f64105f --- /dev/null +++ b/pages/7e1f42/index.html @@ -0,0 +1,179 @@ + + + + + + DesignPattern - 迭代器模式【行为型】 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/7f9fec/index.html b/pages/7f9fec/index.html new file mode 100644 index 000000000..f34a18e7a --- /dev/null +++ b/pages/7f9fec/index.html @@ -0,0 +1,195 @@ + + + + + + iOS - 采集音视频及写入文件 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/804129/index.html b/pages/804129/index.html new file mode 100644 index 000000000..143bb91ed --- /dev/null +++ b/pages/804129/index.html @@ -0,0 +1,226 @@ + + + + + + Flutter - 支持观察NestedScrollView,兼容性更强 😈 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/8102b2/index.html b/pages/8102b2/index.html new file mode 100644 index 000000000..7ee65fac6 --- /dev/null +++ b/pages/8102b2/index.html @@ -0,0 +1,175 @@ + + + + + + Android音视频 - MediaCodec编码mp4踩坑记录 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/810563/index.html b/pages/810563/index.html new file mode 100644 index 000000000..5c1bdbf0b --- /dev/null +++ b/pages/810563/index.html @@ -0,0 +1,112 @@ + + + + + + Flutter - 解决混合开发iOS脚本打包遇到的问题 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/814d28/index.html b/pages/814d28/index.html new file mode 100644 index 000000000..c0515eb65 --- /dev/null +++ b/pages/814d28/index.html @@ -0,0 +1,134 @@ + + + + + + Fastlane - 解决报错Could not find a `ios` simulator | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/8155cd/index.html b/pages/8155cd/index.html new file mode 100644 index 000000000..c1f3c83de --- /dev/null +++ b/pages/8155cd/index.html @@ -0,0 +1,182 @@ + + + + + + Mach-O文件结构分析 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/81ea4e/index.html b/pages/81ea4e/index.html new file mode 100644 index 000000000..740c74935 --- /dev/null +++ b/pages/81ea4e/index.html @@ -0,0 +1,227 @@ + + + + + + 性能优化 - 内存泄漏(3)代码分析篇 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/832f44/index.html b/pages/832f44/index.html new file mode 100644 index 000000000..21f904a90 --- /dev/null +++ b/pages/832f44/index.html @@ -0,0 +1,103 @@ + + + + + + iOS - Swift-UIButton中ImageView的animationImages动画执行完毕后,图标变暗 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/84a12e/index.html b/pages/84a12e/index.html new file mode 100644 index 000000000..7667c197f --- /dev/null +++ b/pages/84a12e/index.html @@ -0,0 +1,294 @@ + + + + + + Gradle入门系列(五) - Gradle其它模块与Plugin插件 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/861244/index.html b/pages/861244/index.html new file mode 100644 index 000000000..fec4bdc24 --- /dev/null +++ b/pages/861244/index.html @@ -0,0 +1,138 @@ + + + + + + Docker - 操作容器 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/867207/index.html b/pages/867207/index.html new file mode 100644 index 000000000..70f20b579 --- /dev/null +++ b/pages/867207/index.html @@ -0,0 +1,113 @@ + + + + + + Docker - 数据管理 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/87435a/index.html b/pages/87435a/index.html new file mode 100644 index 000000000..73e57f549 --- /dev/null +++ b/pages/87435a/index.html @@ -0,0 +1,163 @@ + + + + + + DesignPattern - 代理模式【结构型】 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/88eee8/index.html b/pages/88eee8/index.html new file mode 100644 index 000000000..c45f8dc41 --- /dev/null +++ b/pages/88eee8/index.html @@ -0,0 +1,212 @@ + + + + + + ArrayList源码分析 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/89a4c2/index.html b/pages/89a4c2/index.html new file mode 100644 index 000000000..88e67cc0b --- /dev/null +++ b/pages/89a4c2/index.html @@ -0,0 +1,134 @@ + + + + + + DesignPattern - 外观模式【结构型】 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/89ebcc/index.html b/pages/89ebcc/index.html new file mode 100644 index 000000000..f56462023 --- /dev/null +++ b/pages/89ebcc/index.html @@ -0,0 +1,193 @@ + + + + + + Kotlin - 改良设计模式 - 策略模式 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/8ad3be/index.html b/pages/8ad3be/index.html new file mode 100644 index 000000000..608cc9794 --- /dev/null +++ b/pages/8ad3be/index.html @@ -0,0 +1,250 @@ + + + + + + Android库发布至MavenCentral流程详解 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/8cf0b6/index.html b/pages/8cf0b6/index.html new file mode 100644 index 000000000..10d18a39f --- /dev/null +++ b/pages/8cf0b6/index.html @@ -0,0 +1,293 @@ + + + + + + DesignPattern - 工厂模式【创建型】 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/8e3184/index.html b/pages/8e3184/index.html new file mode 100644 index 000000000..ad159b9c4 --- /dev/null +++ b/pages/8e3184/index.html @@ -0,0 +1,434 @@ + + + + + + 热修复 - 深入浅出原理与实现 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/901745/index.html b/pages/901745/index.html new file mode 100644 index 000000000..632809477 --- /dev/null +++ b/pages/901745/index.html @@ -0,0 +1,186 @@ + + + + + + Kotlin - 类与构造器 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/997f68/index.html b/pages/997f68/index.html new file mode 100644 index 000000000..ea434c0a3 --- /dev/null +++ b/pages/997f68/index.html @@ -0,0 +1,133 @@ + + + + + + iOS - Swift 面向协议编程(一) | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/9a078b/index.html b/pages/9a078b/index.html new file mode 100644 index 000000000..7a2f85a45 --- /dev/null +++ b/pages/9a078b/index.html @@ -0,0 +1,296 @@ + + + + + + Flutter - 使用Pigeon实现视频缓存插件 🐌 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/9a084c/index.html b/pages/9a084c/index.html new file mode 100644 index 000000000..1076d4300 --- /dev/null +++ b/pages/9a084c/index.html @@ -0,0 +1,207 @@ + + + + + + Flutter - ListView与GridView | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/9b40a4/index.html b/pages/9b40a4/index.html new file mode 100644 index 000000000..9f137562d --- /dev/null +++ b/pages/9b40a4/index.html @@ -0,0 +1,256 @@ + + + + + + iOS - 实现25秒完成测试包出包 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/9c63e1/index.html b/pages/9c63e1/index.html new file mode 100644 index 000000000..40bf07732 --- /dev/null +++ b/pages/9c63e1/index.html @@ -0,0 +1,197 @@ + + + + + + Python - 爬虫之数据提取 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/9e247e/index.html b/pages/9e247e/index.html new file mode 100644 index 000000000..9719e3e7e --- /dev/null +++ b/pages/9e247e/index.html @@ -0,0 +1,155 @@ + + + + + + Kotlin - 分支与循环 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/9f1d21/index.html b/pages/9f1d21/index.html new file mode 100644 index 000000000..558fb3d7e --- /dev/null +++ b/pages/9f1d21/index.html @@ -0,0 +1,125 @@ + + + + + + iOS-组件化开发(二):远程私有库的更新与子库 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/a03fed/index.html b/pages/a03fed/index.html new file mode 100644 index 000000000..62f519dd1 --- /dev/null +++ b/pages/a03fed/index.html @@ -0,0 +1,224 @@ + + + + + + DesignPattern - 单例模式【创建型】 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/a0ceba/index.html b/pages/a0ceba/index.html new file mode 100644 index 000000000..e0aeb2f14 --- /dev/null +++ b/pages/a0ceba/index.html @@ -0,0 +1,121 @@ + + + + + + AI - stable-diffusion(AI绘画)的搭建与使用 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/a0e176/index.html b/pages/a0e176/index.html new file mode 100644 index 000000000..abe0aa90a --- /dev/null +++ b/pages/a0e176/index.html @@ -0,0 +1,220 @@ + + + + + + Flutter - 混编项目集成Shorebird热更新🐦(安卓篇) | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/a16954/index.html b/pages/a16954/index.html new file mode 100644 index 000000000..d51183972 --- /dev/null +++ b/pages/a16954/index.html @@ -0,0 +1,169 @@ + + + + + + 安卓第三方登录之微信登录(图文详解) | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/a3228e/index.html b/pages/a3228e/index.html new file mode 100644 index 000000000..7f5e8754a --- /dev/null +++ b/pages/a3228e/index.html @@ -0,0 +1,298 @@ + + + + + + Gradle入门系列(二) - groovy高级语法 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/a39d04/index.html b/pages/a39d04/index.html new file mode 100644 index 000000000..2074e7f9a --- /dev/null +++ b/pages/a39d04/index.html @@ -0,0 +1,446 @@ + + + + + + 使用PorterDuff解决clipPath无法抗锯齿问题 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/a52cff/index.html b/pages/a52cff/index.html new file mode 100644 index 000000000..09ce6782a --- /dev/null +++ b/pages/a52cff/index.html @@ -0,0 +1,127 @@ + + + + + + Kotlin - 枚举与密封类 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/a59099/index.html b/pages/a59099/index.html new file mode 100644 index 000000000..00792d8c6 --- /dev/null +++ b/pages/a59099/index.html @@ -0,0 +1,135 @@ + + + + + + Kotlin - 空类型和智能类型转换 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/a5eb80/index.html b/pages/a5eb80/index.html new file mode 100644 index 000000000..cb99299e0 --- /dev/null +++ b/pages/a5eb80/index.html @@ -0,0 +1,287 @@ + + + + + + 热修复与插件化基础 - Java与Android的类加载器 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/a8e073/index.html b/pages/a8e073/index.html new file mode 100644 index 000000000..75b3d27e1 --- /dev/null +++ b/pages/a8e073/index.html @@ -0,0 +1,184 @@ + + + + + + Kotlin - 类成员 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/a90e8e/index.html b/pages/a90e8e/index.html new file mode 100644 index 000000000..718d6e5b7 --- /dev/null +++ b/pages/a90e8e/index.html @@ -0,0 +1,210 @@ + + + + + + 解决ImageView图片挤压问题 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/a9e62e/index.html b/pages/a9e62e/index.html new file mode 100644 index 000000000..eed21c6ab --- /dev/null +++ b/pages/a9e62e/index.html @@ -0,0 +1,148 @@ + + + + + + Flutter - 打印好用的Debug日志 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/ab3077/index.html b/pages/ab3077/index.html new file mode 100644 index 000000000..4c06de647 --- /dev/null +++ b/pages/ab3077/index.html @@ -0,0 +1,228 @@ + + + + + + DesignPattern - 责任链模式【行为型】 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/ac13fc/index.html b/pages/ac13fc/index.html new file mode 100644 index 000000000..f1d6f5a9e --- /dev/null +++ b/pages/ac13fc/index.html @@ -0,0 +1,381 @@ + + + + + + AndroidNDK - makefile语法详解 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/ad0098/index.html b/pages/ad0098/index.html new file mode 100644 index 000000000..ddca1aa32 --- /dev/null +++ b/pages/ad0098/index.html @@ -0,0 +1,102 @@ + + + + + + Python-虚拟环境 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/b0e486/index.html b/pages/b0e486/index.html new file mode 100644 index 000000000..cc9f4ab76 --- /dev/null +++ b/pages/b0e486/index.html @@ -0,0 +1,318 @@ + + + + + + Swift - PropertyWrapper | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/b1aa38/index.html b/pages/b1aa38/index.html new file mode 100644 index 000000000..8f48f3466 --- /dev/null +++ b/pages/b1aa38/index.html @@ -0,0 +1,99 @@ + + + + + + Swift - 优雅的适配大小 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/b1db2f/index.html b/pages/b1db2f/index.html new file mode 100644 index 000000000..a8892624d --- /dev/null +++ b/pages/b1db2f/index.html @@ -0,0 +1,198 @@ + + + + + + Libgdx - 使用pixmap绘制透明圆角矩形 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/b3bc49/index.html b/pages/b3bc49/index.html new file mode 100644 index 000000000..0dfe13116 --- /dev/null +++ b/pages/b3bc49/index.html @@ -0,0 +1,198 @@ + + + + + + DesignPattern - 备忘录模式【行为型】 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/b46b7a/index.html b/pages/b46b7a/index.html new file mode 100644 index 000000000..86171d348 --- /dev/null +++ b/pages/b46b7a/index.html @@ -0,0 +1,91 @@ + + + + + + AI - AI绘画的精准控图(ControlNet) | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/b576c8/index.html b/pages/b576c8/index.html new file mode 100644 index 000000000..4e7c292b1 --- /dev/null +++ b/pages/b576c8/index.html @@ -0,0 +1,505 @@ + + + + + + Python - 爬虫之Scrapy | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/b5d9a7/index.html b/pages/b5d9a7/index.html new file mode 100644 index 000000000..2f6da7318 --- /dev/null +++ b/pages/b5d9a7/index.html @@ -0,0 +1,140 @@ + + + + + + iOS - 面向协议封装全屏旋转功能 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/b7cd59/index.html b/pages/b7cd59/index.html new file mode 100644 index 000000000..b7d363af1 --- /dev/null +++ b/pages/b7cd59/index.html @@ -0,0 +1,223 @@ + + + + + + Kotlin - 数据类 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/b960c3/index.html b/pages/b960c3/index.html new file mode 100644 index 000000000..9d77c634c --- /dev/null +++ b/pages/b960c3/index.html @@ -0,0 +1,268 @@ + + + + + + Python - 爬虫基础与requests模块 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/ba99fe/index.html b/pages/ba99fe/index.html new file mode 100644 index 000000000..0b0ab0473 --- /dev/null +++ b/pages/ba99fe/index.html @@ -0,0 +1,519 @@ + + + + + + MaterialDesign之SearchView全面解锁 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/bbc079/index.html b/pages/bbc079/index.html new file mode 100644 index 000000000..ee1b0d154 --- /dev/null +++ b/pages/bbc079/index.html @@ -0,0 +1,492 @@ + + + + + + 热修复 - Tinker的集成与使用 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/bea8f9/index.html b/pages/bea8f9/index.html new file mode 100644 index 000000000..8faeb1b32 --- /dev/null +++ b/pages/bea8f9/index.html @@ -0,0 +1,160 @@ + + + + + + iOS - 记录一次对屏幕旋转后崩溃的定位过程 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/beb840/index.html b/pages/beb840/index.html new file mode 100644 index 000000000..1ab062b94 --- /dev/null +++ b/pages/beb840/index.html @@ -0,0 +1,226 @@ + + + + + + Flutter - 列表滚动定位超强辅助库,墙裂推荐!🔥 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/bfa843/index.html b/pages/bfa843/index.html new file mode 100644 index 000000000..e45593c8d --- /dev/null +++ b/pages/bfa843/index.html @@ -0,0 +1,132 @@ + + + + + + Docker - 私有仓库Registry | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/bfc897/index.html b/pages/bfc897/index.html new file mode 100644 index 000000000..7165eb92f --- /dev/null +++ b/pages/bfc897/index.html @@ -0,0 +1,203 @@ + + + + + + DesignPattern - 组合模式【结构型】 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/c243aa/index.html b/pages/c243aa/index.html new file mode 100644 index 000000000..3fd5908a4 --- /dev/null +++ b/pages/c243aa/index.html @@ -0,0 +1,112 @@ + + + + + + iOS - Swift 高仿微信 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/c53d80/index.html b/pages/c53d80/index.html new file mode 100644 index 000000000..74186500b --- /dev/null +++ b/pages/c53d80/index.html @@ -0,0 +1,183 @@ + + + + + + iOS - 视频采集详解 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/c5b883/index.html b/pages/c5b883/index.html new file mode 100644 index 000000000..47ad090ad --- /dev/null +++ b/pages/c5b883/index.html @@ -0,0 +1,164 @@ + + + + + + Kotlin - 数据类型 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/c7c2c0/index.html b/pages/c7c2c0/index.html new file mode 100644 index 000000000..ecc630ae0 --- /dev/null +++ b/pages/c7c2c0/index.html @@ -0,0 +1,230 @@ + + + + + + Kotlin - 伴生对象与静态成员 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/c7d38a/index.html b/pages/c7d38a/index.html new file mode 100644 index 000000000..da91272fd --- /dev/null +++ b/pages/c7d38a/index.html @@ -0,0 +1,314 @@ + + + + + + MaterialDesign之AppBarLayout与CollapsingToolbarLayout的学习 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/cb27eb/index.html b/pages/cb27eb/index.html new file mode 100644 index 000000000..56dc02bcb --- /dev/null +++ b/pages/cb27eb/index.html @@ -0,0 +1,132 @@ + + + + + + iOS - Swift 仿微信小红点(无数字) | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/cc54c3/index.html b/pages/cc54c3/index.html new file mode 100644 index 000000000..5481d8316 --- /dev/null +++ b/pages/cc54c3/index.html @@ -0,0 +1,90 @@ + + + + + + iOS - 解决Transporter一直卡正在验证的问题 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/ccf9c0/index.html b/pages/ccf9c0/index.html new file mode 100644 index 000000000..1b4c3127e --- /dev/null +++ b/pages/ccf9c0/index.html @@ -0,0 +1,107 @@ + + + + + + 热修复与插件化基础 - dex与class | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/cd4aeb/index.html b/pages/cd4aeb/index.html new file mode 100644 index 000000000..566a12484 --- /dev/null +++ b/pages/cd4aeb/index.html @@ -0,0 +1,570 @@ + + + + + + RePlugin集成ARouter | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/cd4fd1/index.html b/pages/cd4fd1/index.html new file mode 100644 index 000000000..1b4d7bc77 --- /dev/null +++ b/pages/cd4fd1/index.html @@ -0,0 +1,134 @@ + + + + + + Flutter - 快速实现聊天会话列表的效果,完美💯 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/d09c0b/index.html b/pages/d09c0b/index.html new file mode 100644 index 000000000..4eb29d679 --- /dev/null +++ b/pages/d09c0b/index.html @@ -0,0 +1,201 @@ + + + + + + iOS - ARC与MRC的单例设计模式 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/d0a26b/index.html b/pages/d0a26b/index.html new file mode 100644 index 000000000..97af15065 --- /dev/null +++ b/pages/d0a26b/index.html @@ -0,0 +1,194 @@ + + + + + + iOS - Swift UICollectionView横向分页滚动,cell左右排版 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/d18561/index.html b/pages/d18561/index.html new file mode 100644 index 000000000..878acf365 --- /dev/null +++ b/pages/d18561/index.html @@ -0,0 +1,176 @@ + + + + + + Flutter - 秒杀1/2曝光统计 📊 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/d45f5d/index.html b/pages/d45f5d/index.html new file mode 100644 index 000000000..89bbf22e7 --- /dev/null +++ b/pages/d45f5d/index.html @@ -0,0 +1,380 @@ + + + + + + Kotlin - 改良设计模式 - 工厂模式 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/d4ac48/index.html b/pages/d4ac48/index.html new file mode 100644 index 000000000..deac39383 --- /dev/null +++ b/pages/d4ac48/index.html @@ -0,0 +1,136 @@ + + + + + + iOS-组件化开发(一):远程私有库的基本使用 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/d85c7a/index.html b/pages/d85c7a/index.html new file mode 100644 index 000000000..7958edb47 --- /dev/null +++ b/pages/d85c7a/index.html @@ -0,0 +1,719 @@ + + + + + + Gradle入门系列(一) - groovy基础语法 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/daa62e/index.html b/pages/daa62e/index.html new file mode 100644 index 000000000..560443851 --- /dev/null +++ b/pages/daa62e/index.html @@ -0,0 +1,184 @@ + + + + + + iOS - 解决SecurityEnvSDK与SGMain的冲突问题 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/db2b0b/index.html b/pages/db2b0b/index.html new file mode 100644 index 000000000..2f4033502 --- /dev/null +++ b/pages/db2b0b/index.html @@ -0,0 +1,641 @@ + + + + + + 手撸一个简易Android数据库框架 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/dd574d/index.html b/pages/dd574d/index.html new file mode 100644 index 000000000..924dd7b60 --- /dev/null +++ b/pages/dd574d/index.html @@ -0,0 +1,279 @@ + + + + + + Kotlin - 改良设计模式 - 构建者模式 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/defa2f/index.html b/pages/defa2f/index.html new file mode 100644 index 000000000..f227a6bd8 --- /dev/null +++ b/pages/defa2f/index.html @@ -0,0 +1,219 @@ + + + + + + Kotlin - 常见高阶函数 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/df8033/index.html b/pages/df8033/index.html new file mode 100644 index 000000000..c7110e6fa --- /dev/null +++ b/pages/df8033/index.html @@ -0,0 +1,181 @@ + + + + + + iOS - 实现UINavigation全屏滑动返回(二) | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/dfd4b7/index.html b/pages/dfd4b7/index.html new file mode 100644 index 000000000..9980dd10f --- /dev/null +++ b/pages/dfd4b7/index.html @@ -0,0 +1,109 @@ + + + + + + iOS - 解决 Could not find 'faraday' ... gem(s) (Gem::LoadError) | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/e06ece/index.html b/pages/e06ece/index.html new file mode 100644 index 000000000..83df6bc08 --- /dev/null +++ b/pages/e06ece/index.html @@ -0,0 +1,311 @@ + + + + + + iOS - RxSwift项目实战记录 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/e13e4e/index.html b/pages/e13e4e/index.html new file mode 100644 index 000000000..6f00cb5cc --- /dev/null +++ b/pages/e13e4e/index.html @@ -0,0 +1,217 @@ + + + + + + DesignPattern - 策略模式【行为型】 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/e18f51/index.html b/pages/e18f51/index.html new file mode 100644 index 000000000..4af7aa45d --- /dev/null +++ b/pages/e18f51/index.html @@ -0,0 +1,126 @@ + + + + + + Kotlin - 扩展成员 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/e3339e/index.html b/pages/e3339e/index.html new file mode 100644 index 000000000..42904446f --- /dev/null +++ b/pages/e3339e/index.html @@ -0,0 +1,280 @@ + + + + + + Android脚本 - 不一样的Gradle多渠道配置总结 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/e3a33a/index.html b/pages/e3a33a/index.html new file mode 100644 index 000000000..7327e6f97 --- /dev/null +++ b/pages/e3a33a/index.html @@ -0,0 +1,91 @@ + + + + + + AI - stable-diffusion 艺术化二维码 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/e3a5cf/index.html b/pages/e3a5cf/index.html new file mode 100644 index 000000000..c66d66fdd --- /dev/null +++ b/pages/e3a5cf/index.html @@ -0,0 +1,144 @@ + + + + + + 性能优化 - 内存泄漏(1)入门篇 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/e3acb4/index.html b/pages/e3acb4/index.html new file mode 100644 index 000000000..1760aad47 --- /dev/null +++ b/pages/e3acb4/index.html @@ -0,0 +1,132 @@ + + + + + + Docker - Compose的使用 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/e3eb8a/index.html b/pages/e3eb8a/index.html new file mode 100644 index 000000000..35c26801a --- /dev/null +++ b/pages/e3eb8a/index.html @@ -0,0 +1,271 @@ + + + + + + Gradle入门系列(四) - 初识Gradle Task | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/e4ed73/index.html b/pages/e4ed73/index.html new file mode 100644 index 000000000..410c271c1 --- /dev/null +++ b/pages/e4ed73/index.html @@ -0,0 +1,1095 @@ + + + + + + Android音视频 - Libyuv使用实战 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/e6f2c4/index.html b/pages/e6f2c4/index.html new file mode 100644 index 000000000..e32db9f1f --- /dev/null +++ b/pages/e6f2c4/index.html @@ -0,0 +1,205 @@ + + + + + + Flutter - 混编项目集成Shorebird热更新🐦(iOS篇) | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/e87d2b/index.html b/pages/e87d2b/index.html new file mode 100644 index 000000000..a0f7d7bb1 --- /dev/null +++ b/pages/e87d2b/index.html @@ -0,0 +1,213 @@ + + + + + + Flutter - 聊天输入框更新文本时的必备优化点🔖 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/e987b9/index.html b/pages/e987b9/index.html new file mode 100644 index 000000000..1a4fb0093 --- /dev/null +++ b/pages/e987b9/index.html @@ -0,0 +1,266 @@ + + + + + + Android - 利用 jitpack 免费发布闭源 aar | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/e9d226/index.html b/pages/e9d226/index.html new file mode 100644 index 000000000..377130489 --- /dev/null +++ b/pages/e9d226/index.html @@ -0,0 +1,201 @@ + + + + + + DesignPattern - 建造者模式【创建型】 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/ea0f4e/index.html b/pages/ea0f4e/index.html new file mode 100644 index 000000000..337a1a251 --- /dev/null +++ b/pages/ea0f4e/index.html @@ -0,0 +1,635 @@ + + + + + + uniapp - 接入科大讯飞语音评测 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/ea5b48/index.html b/pages/ea5b48/index.html new file mode 100644 index 000000000..951bc53a8 --- /dev/null +++ b/pages/ea5b48/index.html @@ -0,0 +1,96 @@ + + + + + + 逆向 - 用IDA破解adb时间限制 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/ea91be/index.html b/pages/ea91be/index.html new file mode 100644 index 000000000..e4c1938a6 --- /dev/null +++ b/pages/ea91be/index.html @@ -0,0 +1,151 @@ + + + + + + Kotlin - 作用域函数 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/ee17d1/index.html b/pages/ee17d1/index.html new file mode 100644 index 000000000..724c12673 --- /dev/null +++ b/pages/ee17d1/index.html @@ -0,0 +1,193 @@ + + + + + + Kotlin - 函数与Lambda表达式 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/f2b19d/index.html b/pages/f2b19d/index.html new file mode 100644 index 000000000..096fc7aff --- /dev/null +++ b/pages/f2b19d/index.html @@ -0,0 +1,332 @@ + + + + + + Android脚本 - 不一样的Gradle多渠道配置总结2 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/f2ddfa/index.html b/pages/f2ddfa/index.html new file mode 100644 index 000000000..e067387d6 --- /dev/null +++ b/pages/f2ddfa/index.html @@ -0,0 +1,147 @@ + + + + + + Mac上pyenv的安装与使用 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/f63a8b/index.html b/pages/f63a8b/index.html new file mode 100644 index 000000000..a1e5450c4 --- /dev/null +++ b/pages/f63a8b/index.html @@ -0,0 +1,159 @@ + + + + + + RePlugin集成AndroidAutoSize | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/f6869d/index.html b/pages/f6869d/index.html new file mode 100644 index 000000000..0c4c17ebd --- /dev/null +++ b/pages/f6869d/index.html @@ -0,0 +1,431 @@ + + + + + + Jsonnet - json数据模板语言 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/f6bde9/index.html b/pages/f6bde9/index.html new file mode 100644 index 000000000..5393aef3c --- /dev/null +++ b/pages/f6bde9/index.html @@ -0,0 +1,104 @@ + + + + + + iOS - Swift-UICollectionView横向分页的问题 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/f6d976/index.html b/pages/f6d976/index.html new file mode 100644 index 000000000..20038d422 --- /dev/null +++ b/pages/f6d976/index.html @@ -0,0 +1,243 @@ + + + + + + Flutter - 升级3.19之后页面多次rebuild?🤨 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/f81235/index.html b/pages/f81235/index.html new file mode 100644 index 000000000..14ff0a5e5 --- /dev/null +++ b/pages/f81235/index.html @@ -0,0 +1,122 @@ + + + + + + vite - 多渠道差异化打包插件 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/f81fb5/index.html b/pages/f81fb5/index.html new file mode 100644 index 000000000..24998a5af --- /dev/null +++ b/pages/f81fb5/index.html @@ -0,0 +1,112 @@ + + + + + + iOS逆向 - 运行时分析(一)class-dump | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/f98f41/index.html b/pages/f98f41/index.html new file mode 100644 index 000000000..e5a9f98f8 --- /dev/null +++ b/pages/f98f41/index.html @@ -0,0 +1,161 @@ + + + + + + Flutter - 聊天键盘与面板丝滑切换的强势升级 🍻 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/fb0480/index.html b/pages/fb0480/index.html new file mode 100644 index 000000000..94779bd5d --- /dev/null +++ b/pages/fb0480/index.html @@ -0,0 +1,137 @@ + + + + + + iOS - Swift面向协议编程(二) | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/fc665a/index.html b/pages/fc665a/index.html new file mode 100644 index 000000000..481872d61 --- /dev/null +++ b/pages/fc665a/index.html @@ -0,0 +1,671 @@ + + + + + + iOS逆向 - 运行时分析(三)Frida | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/fc879e/index.html b/pages/fc879e/index.html new file mode 100644 index 000000000..f0235bada --- /dev/null +++ b/pages/fc879e/index.html @@ -0,0 +1,246 @@ + + + + + + DesignPattern - 装饰器模式【结构型】 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/fcfa28/index.html b/pages/fcfa28/index.html new file mode 100644 index 000000000..fe5a12009 --- /dev/null +++ b/pages/fcfa28/index.html @@ -0,0 +1,223 @@ + + + + + + Kotlin - 改良设计模式 - 观察者模式 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/fe4c94/index.html b/pages/fe4c94/index.html new file mode 100644 index 000000000..f68bf73e5 --- /dev/null +++ b/pages/fe4c94/index.html @@ -0,0 +1,164 @@ + + + + + + MaterialDesign之学一波Palette | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/pages/feeb2d/index.html b/pages/feeb2d/index.html new file mode 100644 index 000000000..2ec3a407d --- /dev/null +++ b/pages/feeb2d/index.html @@ -0,0 +1,216 @@ + + + + + + Kotlin - 面向对象之继承与实现 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/power/index.html b/power/index.html new file mode 100644 index 000000000..84befd6d6 --- /dev/null +++ b/power/index.html @@ -0,0 +1,89 @@ + + + + + + 内功心法 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/tags/index.html b/tags/index.html new file mode 100644 index 000000000..503a625de --- /dev/null +++ b/tags/index.html @@ -0,0 +1,82 @@ + + + + + + 标签 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + + diff --git a/web/index.html b/web/index.html new file mode 100644 index 000000000..d9287a28e --- /dev/null +++ b/web/index.html @@ -0,0 +1,103 @@ + + + + + + 前端 | FSA全栈行动 + + + + + + + + + + + + + + + +
+ + +