<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Flutter on 首页 | Widegram</title>
    <link>https://widegram.com/tags/flutter/</link>
    <description>Recent content in Flutter on 首页 | Widegram</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Wed, 15 Dec 2021 14:08:45 +0000</lastBuildDate><atom:link href="https://widegram.com/tags/flutter/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Backup of Flutter Cupertino Icons&#39; Gallery</title>
      <link>https://widegram.com/2021/12/Flutter-Cupertino-Icons/</link>
      <pubDate>Wed, 15 Dec 2021 14:08:45 +0000</pubDate>
      
      <guid>https://widegram.com/2021/12/Flutter-Cupertino-Icons/</guid>
      <description> </description>
      <content:encoded><![CDATA[<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfzhsrlj31er0u0acj.jpg" alt="截屏2021-12-15 下午1.52.27"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfqhm7kj31av0u077d.jpg" alt="截屏2021-12-15 下午1.52.47"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg1cr9bj31ab0u0tbs.jpg" alt="截屏2021-12-15 下午1.52.58"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfz1it4j31aa0u0gos.jpg" alt="截屏2021-12-15 下午1.53.15"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfva2n0j319t0u0jux.jpg" alt="截屏2021-12-15 下午1.53.27"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg0voo0j31b00u0wgv.jpg" alt="截屏2021-12-15 下午1.53.46"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfng6c9j319e0u0acf.jpg" alt="截屏2021-12-15 下午1.55.17"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehgx9f8cj319j0u0mzi.jpg" alt="截屏2021-12-15 下午1.54.13"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg2rf4yj319p0u0acb.jpg" alt="截屏2021-12-15 下午1.55.29"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfx5piaj31cg0u0q5j.jpg" alt="截屏2021-12-15 下午1.56.29"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfvr5yej31am0u0mzm.jpg" alt="截屏2021-12-15 下午1.56.37"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfucsy9j31be0u0acm.jpg" alt="截屏2021-12-15 下午1.54.22"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfsxms9j31980u0jtp.jpg" alt="截屏2021-12-15 下午1.55.49"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfoqvytj31an0u0tax.jpg" alt="截屏2021-12-15 下午1.56.45"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfp6v71j31a50u00vd.jpg" alt="截屏2021-12-15 下午1.54.33"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg385gwj31al0u0jtv.jpg" alt="截屏2021-12-15 下午1.55.57"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfsgyywj31bj0u0mzq.jpg" alt="截屏2021-12-15 下午1.56.53"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehftvlcaj319u0u0acv.jpg" alt="截屏2021-12-15 下午1.55.07"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg007jhj31ag0u0q5d.jpg" alt="截屏2021-12-15 下午1.56.10"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfy37jqj31bz0u0di2.jpg" alt="截屏2021-12-15 下午1.57.01"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfrjje1j31b90u00v6.jpg" alt="截屏2021-12-15 下午1.57.10"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfns1ysj31bh0u0gnq.jpg" alt="截屏2021-12-15 下午1.57.19"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg557okj31ay0u0whe.jpg" alt="截屏2021-12-15 下午1.57.39"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg0f0iwj31an0u0acy.jpg" alt="截屏2021-12-15 下午1.57.48"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfusj40j31ar0u0tb4.jpg" alt="截屏2021-12-15 下午1.58.03"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg5n0p6j31bk0u0whm.jpg" alt="截屏2021-12-15 下午1.58.11"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg6200nj31bi0u0tbp.jpg" alt="截屏2021-12-15 下午1.58.18"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfw88g9j31ci0u0tb2.jpg" alt="截屏2021-12-15 下午1.58.30"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg1u7wyj31bf0u0did.jpg" alt="截屏2021-12-15 下午1.58.37"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg3rffjj31c00u0div.jpg" alt="截屏2021-12-15 下午1.58.45"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehgy4vt9j31bq0u0go0.jpg" alt="截屏2021-12-15 下午1.58.51"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfxnem3j31ar0u040q.jpg" alt="截屏2021-12-15 下午1.58.56"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfr31e5j31b20u0q55.jpg" alt="截屏2021-12-15 下午1.59.01"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg2b9bdj31d20u0wgo.jpg" alt="截屏2021-12-15 下午1.59.15"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfthcbij31cz0u00v7.jpg" alt="截屏2021-12-15 下午1.59.20"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfyhof8j309m06w3ya.jpg" alt="截屏2021-12-15 下午1.59.26"  />
</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Flutter插件列表</title>
      <link>https://widegram.com/2021/11/flutter-plugins/</link>
      <pubDate>Mon, 29 Nov 2021 00:00:00 +0000</pubDate>
      
      <guid>https://widegram.com/2021/11/flutter-plugins/</guid>
      <description>通用零部件
按钮 flutter_awesome_buttons | Flutter Package (flutter-io.cn) 文字格式检测 string_validator | Dart Package (flutter-io.cn) 本地存储 objectbox | Dart Package (flutter-io.cn) ffloat/README_CN.md at master · Fliggy-Mobile/ffloat (github.com) Slideable flutter_slidable | Flutter Package (flutter-io.cn) 文字动画效果 animated_text_kit | Flutter Package (flutter-io.cn) 分享页面 share_plus | Flutter Package (flutter-io.cn) flutter_share | Flutter Package (flutter-io.cn) !!scrollbar flutter_improved_scrolling | Flutter Package (flutter-io.cn) vs_scrollbar | Flutter Package (flutter-io.cn) scroll_to_id | Flutter Package (flutter-io.cn) 下拉加载更多 infinite_scroll_pagination | Flutter Package (flutter-io.cn) loadmore | Flutter Package (flutter-io.</description>
      <content:encoded><![CDATA[<p>通用零部件</p>
<ul>
<li>按钮
<ul>
<li>flutter_awesome_buttons | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>文字格式检测 string_validator | Dart Package (flutter-io.cn)</li>
<li>本地存储 objectbox | Dart Package (flutter-io.cn)</li>
<li>ffloat/README_CN.md at master · Fliggy-Mobile/ffloat (github.com)</li>
<li>Slideable flutter_slidable | Flutter Package (flutter-io.cn)</li>
<li>文字动画效果 animated_text_kit | Flutter Package (flutter-io.cn)</li>
<li>分享页面 share_plus | Flutter Package (flutter-io.cn)
<ul>
<li>flutter_share | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>!!scrollbar flutter_improved_scrolling | Flutter Package (flutter-io.cn)
<ul>
<li>vs_scrollbar | Flutter Package (flutter-io.cn)</li>
<li>scroll_to_id | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>下拉加载更多 infinite_scroll_pagination | Flutter Package (flutter-io.cn)
<ul>
<li>loadmore | Flutter Package (flutter-io.cn)</li>
<li>very_good_infinite_list | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>tooltip just_the_tooltip | Flutter Package (flutter-io.cn)</li>
<li>位置信息 location | Flutter Package (flutter-io.cn)</li>
<li>apple id登陆 sign_in_with_apple | Flutter Package (flutter-io.cn)</li>
<li>背景模糊 flutter_blurhash | Flutter Package (flutter-io.cn)</li>
<li>加载中 sleek_circular_slider | Flutter Package (flutter-io.cn)
<ul>
<li>progress_indicators | Flutter Package (flutter-io.cn)</li>
<li>skeleton_loader | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>升级提醒 upgrader | Flutter Package (flutter-io.cn)</li>
<li>html渲染 flutter_widget_from_html | Flutter Package (flutter-io.cn)</li>
<li>选择器 direct_select_flutter | Flutter Package (flutter-io.cn)</li>
<li>不同大小的屏幕适应 layout | Flutter Package (flutter-io.cn)</li>
<li>snackbar
<ul>
<li>top_snackbar_flutter | Flutter Package (flutter-io.cn)</li>
<li>无context one_context | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>截屏时更多选项！feedback | Flutter Package (flutter-io.cn)</li>
<li>底部自动隐藏！scroll_bottom_navigation_bar | Flutter Package (flutter-io.cn)</li>
<li>测试
<ul>
<li>测试所有页面 flutter_storyboard | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>扫描二维码 barcode_scan2 | Flutter Package (flutter-io.cn)</li>
<li>内部webview flutter_custom_tabs | Flutter Package (flutter-io.cn)
登录页面</li>
<li>bluemix/Gradient-Screens: 🌈 Gradients applied to buttons, texts and backgrounds in Flutter (github.com)</li>
<li>使用教程 coachmaker | Flutter Package (flutter-io.cn)</li>
<li></li>
</ul>
<p>开屏加载页面</p>
<ul>
<li>flutter_native_splash | Dart Package (flutter-io.cn)</li>
<li>卡片式后台切换 flutter_swiper | Flutter Package (flutter-io.cn)</li>
<li>选择关注的标签 multi_select_flutter | Flutter Package (flutter-io.cn)
第一次启动时的页面指引</li>
<li>fancy_on_boarding | Flutter Package (flutter-io.cn)</li>
<li>intro_slider | Flutter Package (flutter-io.cn)
社区页面</li>
<li>reorder 选择关注的社区：filter_list | Flutter Package (flutter-io.cn)</li>
<li>从新排序 reorderables | Flutter Package (flutter-io.cn)</li>
<li>顶部切换器 bubble_tab_indicator | Flutter Package (flutter-io.cn)
设置页面</li>
<li>cupertino_list_tile | Flutter Package (flutter-io.cn)
用户信息主页</li>
<li>点击展开属性chip star_menu | Flutter Package (flutter-io.cn)</li>
<li>更多信息 solid_bottom_sheet | Flutter Package (flutter-io.cn)</li>
<li>sliverbar_with_card | Flutter Package (flutter-io.cn)</li>
<li></li>
</ul>
<p>Markdown编辑器</p>
<ul>
<li>撤销/重做 undo | Dart Package (flutter-io.cn)</li>
<li>键盘定制！keyboard_actions | Flutter Package (flutter-io.cn)</li>
<li></li>
</ul>
<p>同步</p>
<ul>
<li>flutter_wordpress | Flutter Package (flutter-io.cn)
图库</li>
<li>点按多选
消息页面</li>
<li>tabbar scroll_navigation | Flutter Package (flutter-io.cn)
FAB</li>
<li>按钮
<ul>
<li>不可拖动上滑自动隐藏，下滑自动出现：gradient_fab | Flutter Package (flutter-io.cn)</li>
<li>可拖动，滑动时隐藏：draggable_float_widget | Flutter Package (flutter-io.cn)</li>
<li>拖动，自动吸附，不自动隐藏：assistive_touch | Flutter Package (flutter-io.cn)</li>
<li>floatingpanel | Flutter Package (flutter-io.cn)</li>
<li>搜索！Imgkl/anim_search_bar: A flutter package that has an animated search bar with loads of customization (github.com)</li>
</ul>
</li>
<li>展开
<ul>
<li>全页面展开 animated_fab_button_menu | Flutter Package (flutter-io.cn)</li>
<li>展开+文字
<ul>
<li>flutter_speed_dial | Flutter Package (flutter-io.cn)</li>
<li>floating_action_bubble | Flutter Package (flutter-io.cn)</li>
<li>simple_speed_dial | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>写动态！ floaty_head | Flutter Package (flutter-io.cn)
<ul>
<li>flash | Flutter Package (flutter-io.cn)</li>
<li></li>
</ul>
</li>
</ul>
</li>
</ul>
<p>toast</p>
<ul>
<li>fluttertoast | Flutter Package (flutter-io.cn)</li>
</ul>
<p>Flutter：</p>
<ul>
<li>MD编辑器：BertrandBev/code_field: A customizable code text field supporting syntax highlighting (github.com)
<ul>
<li>解决Flutter拼音输入过程回调问题 - 掘金 (juejin.cn)</li>
</ul>
</li>
<li>MD预览：flutter_markdown | Flutter Package (flutter-io.cn)</li>
<li>GITHUB API：github | Dart Package (flutter-io.cn)</li>
<li>debug
<ul>
<li>flutter_ume | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>！“社区”页面scroll_navigation | Flutter Package (flutter-io.cn)</li>
<li>icons
<ul>
<li>microsoft/fluentui-system-icons: Fluent System Icons are a collection of familiar, friendly and modern icons from Microsoft. (github.com)</li>
<li>Icons | Font Awesome</li>
</ul>
</li>
<li>头像装饰</li>
<li>iPad版 macOS界面 macos_ui | Flutter Package (flutter-io.cn)
<ul>
<li>device_simulator | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>UI 套件
<ul>
<li>getwidget | Flutter Package (flutter-io.cn)</li>
<li>gradient_ui_widgets | Flutter Package (flutter-io.cn)</li>
<li>flutter_awesome_buttons | Flutter Package (flutter-io.cn)</li>
<li>velocity_x | Flutter Package (flutter-io.cn)</li>
<li>fluent_ui | Flutter Package (flutter-io.cn)</li>
<li>配色 flex_color_scheme | Flutter Package (flutter-io.cn)</li>
<li>flutter_stripe | Flutter Package (flutter-io.cn)</li>
<li>flui/README-zh_CN.md at master · Rannie/flui (github.com)</li>
</ul>
</li>
<li>设置页面选项
<ul>
<li>cupertino_list_tile | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>刷新完成 snackbar
<ul>
<li>another_flushbar | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>撤销/重做 undo | Dart Package (flutter-io.cn)</li>
<li>点击展开菜单 star_menu | Flutter Package (flutter-io.cn)</li>
<li>作者个人主页
<ul>
<li>draggable_home | Flutter Package (flutter-io.cn)</li>
<li>天气背景（表达心情、作者所在当地天气表达与之的连接）flutter_weather_bg_null_safety | Flutter Package (flutter-io.cn)</li>
<li>（评论页面）sliding_panel_pro | Flutter Package (flutter-io.cn)
<ul>
<li>floating_pullup_card | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
</ul>
</li>
<li>搜索页面
<ul>
<li>search_page | Flutter Package (flutter-io.cn)</li>
<li>material_floating_search_bar | Flutter Package (flutter-io.cn)</li>
<li>floating_search_bar | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>页面跳转动画
<ul>
<li>swipeable_page_route | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>边缘滑动更多操作
<ul>
<li>slidable_bar | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>下拉刷新
<ul>
<li>flutter_easyrefresh | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>特性介绍
<ul>
<li>flutter_onboarding_slider | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>关注列表（快速跳转）azlistview | Flutter Package (flutter-io.cn)
<ul>
<li>scrollable_list_tabview | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>文字 + circularrefresh
<ul>
<li>flutter_hud | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>ListTile GroovinChip/groovin_widgets: A Flutter package containing widgets created by GroovinChip. (github.com)</li>
<li>fab：
<ul>
<li>自动吸附边缘、可拖动、滑动自动隐藏：（！）gradient_fab | Flutter Package (flutter-io.cn)
<ul>
<li>draggable_float_widget | Flutter Package (flutter-io.cn)</li>
<li>draggable_fab | Flutter Package (flutter-io.cn)</li>
<li>assistive_touch | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>animated_fab_button_menu | Flutter Package (flutter-io.cn)</li>
<li>speed_dial_fab | Flutter Package (flutter-io.cn)</li>
<li>hawk_fab_menu | Flutter Package (flutter-io.cn)</li>
<li>animated_floating_buttons | Flutter Package (flutter-io.cn)</li>
<li>flutter_speed_dial | Flutter Package (flutter-io.cn)</li>
<li>floating_action_bubble | Flutter Package (flutter-io.cn)</li>
<li>simple_speed_dial | Flutter Package (flutter-io.cn)</li>
<li>Mohanraj153/flutter_boom_menu: Flutter plugin to implement a Boom Menu, with icons, title, subtitle, animated FAB Icons and hide on scrolling. (github.com)</li>
</ul>
</li>
<li>底部 floating_bottom_navigation_bar | Flutter Package (flutter-io.cn)
<ul>
<li>convex_bottom_bar/README-zh.md at master · hacktons/convex_bottom_bar (github.com)</li>
<li>floating_navbar | Flutter Package (flutter-io.cn)</li>
<li>curved_navigation_bar | Flutter Package (flutter-io.cn)</li>
<li>flutter_snake_navigationbar | Flutter Package (flutter-io.cn)</li>
<li>persistent_bottom_nav_bar | Flutter Package (flutter-io.cn)</li>
<li>！custom_navigation_bar | Flutter Package (flutter-io.cn)</li>
<li>animated_bottom_navigation_bar | Flutter Package (flutter-io.cn)</li>
<li>bottom_navy_bar | Flutter Package (flutter-io.cn)</li>
<li>salomon_bottom_bar | Flutter Package (flutter-io.cn)</li>
<li>！bubble_bottom_bar | Flutter Package (flutter-io.cn)</li>
<li>bottom_personalized_dot_bar | Flutter Package (flutter-io.cn)</li>
<li>cuberto_bottom_bar | Flutter Package (flutter-io.cn)</li>
<li>extended_navbar_scaffold | Flutter Package (flutter-io.cn)</li>
<li>bottom_bar | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>！私聊界面 + 个人心情列表
<ul>
<li>sticky_grouped_list | Flutter Package (flutter-io.cn)</li>
<li>papercups_flutter | Flutter Package (flutter-io.cn)</li>
<li>grouped_list | Flutter Package (flutter-io.cn)</li>
<li>dash_chat | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>navbar
<ul>
<li>dot_navigation_bar | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>tabbar
<ul>
<li>tab_indicator_styler | Flutter Package (flutter-io.cn)</li>
<li>buttons_tabbar | Flutter Package (flutter-io.cn)</li>
<li>shifting_tabbar | Flutter Package (flutter-io.cn)</li>
<li>scrollable_list_tabview | Flutter Package (flutter-io.cn)</li>
<li>aliyigitbireroglu/flutter-cupertino-tabbar (github.com)</li>
<li>vertical_tab_bar_view | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>drawer
<ul>
<li>drawerbehavior | Flutter Package (flutter-io.cn)</li>
<li>curved_drawer_fork | Flutter Package (flutter-io.cn)</li>
<li>backdrop | Flutter Package (flutter-io.cn)</li>
<li>flutter_inner_drawer | Flutter Package (flutter-io.cn)</li>
<li>flutter_slider_drawer | Flutter Package (flutter-io.cn)</li>
<li>drawer_swipe | Flutter Package (flutter-io.cn)</li>
<li>shrink_sidemenu | Flutter Package (flutter-io.cn)</li>
<li>foldable_sidebar | Flutter Package (flutter-io.cn)</li>
<li>collapsible_sidebar | Flutter Package (flutter-io.cn)</li>
<li>树形展开 flutter_admin_scaffold | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>评论
<ul>
<li>查看评论 sliding_up_panel | Flutter Package (flutter-io.cn)</li>
<li>点击评论 flutter_overlay | Flutter Package (flutter-io.cn)</li>
<li>bottom_bar_with_sheet | Flutter Package (flutter-io.cn)</li>
<li>sliding_sheet | Flutter Package (flutter-io.cn)</li>
<li>expandable_bottom_bar | Flutter Package (flutter-io.cn)</li>
<li>we_slide | Flutter Package (flutter-io.cn)</li>
<li>随键盘弹出！keyboard_attachable | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>登陆界面动态背景 floating_bubbles | Flutter Package (flutter-io.cn)
<ul>
<li>passwordfield | Flutter Package (flutter-io.cn)</li>
<li>有趣的动画效果 dough | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>markdown功能
<ul>
<li>radar_chart | Flutter Package (flutter-io.cn)</li>
<li>graphview</li>
<li>生成pdf pdf | Dart Package (flutter-io.cn)</li>
<li>打开地图 map_launcher | Flutter Package (flutter-io.cn)</li>
<li>图像编辑 image_editor_pro | Flutter Package (flutter-io.cn)</li>
<li>!点击图片查看大图overscroll_pop | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>心情/评论显示框textspan
<ul>
<li>extended_text | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>Scaffold master_detail_scaffold | Flutter Package (flutter-io.cn)</li>
<li>教学
<ul>
<li>overlay_tutorial | Flutter Package (flutter-io.cn)</li>
<li></li>
</ul>
</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Flutter 异步全解</title>
      <link>https://widegram.com/2021/11/flutter-async/</link>
      <pubDate>Tue, 09 Nov 2021 21:19:00 +0000</pubDate>
      
      <guid>https://widegram.com/2021/11/flutter-async/</guid>
      <description>一、异步入门 1.1 async和await 这两个关键词的本质是语法糖。例如：
void _increment() async { await Future.delayed(Duration(seconds: 1)); setState((){ _counter++; }); } 等价于：
void _increment() { Future.delayed(Duration(seconds: 1),(){ setState((){ _counter++; }); }); } 或者：
void _increment() { Future.delayed(Duration(seconds: 1)).then( (value) =&amp;gt; setState((){ _counter++; }) ); } ​	这里需要纠正一个误区：就像JS那样，dart的异步操作并非多线程。在dart中，每一个线程都被isolate包裹，它们之间不能共享内存，也就避免了死锁问题，降低了程序复杂度，提升GC的性能。线程之间使用消息队列来通信。
​	一个线程怎么处理等待事件呢？这里就要用到Event Loop机制。内置一个Event Queue和MicroTask Queue，只要MicroTask Queue中还有事件没有处理完，则先处理这里面的事件，而此时EventQueue中的事件一个都不会被执行。使用scheduleMicrotask()来向Microtask Queue添加事件。
void main(){ Future(() =&amp;gt; print(&amp;#39;A&amp;#39;)); print(&amp;#39;B&amp;#39;); } //打印结果：BA ​	这里我们还是要提醒一下，只有在出现需要等待的事件时，EventLoop机制才能发挥作用。如果卡顿是因为计算量过大造成的，那么只有多开几个Isolate（线程）来解决了。
1.2 运行时机 直接运行：Future.sync()、Future.value()、_.then()
Microtask：scheduleMicrotask()、Future.microtask()、_completed.then()
Event：Future()、Future.delayed()
1.3 运行时机举例 直接运行
void main(){ print(&amp;#34;1&amp;#34;); Future.value(func()); print(&amp;#34;3&amp;#34;); } String func(){ print(&amp;#34;2&amp;#34;); return &amp;#34;&amp;#34;; }//输出结果：123 void main(){ print(&amp;#34;1&amp;#34;); Future.</description>
      <content:encoded><![CDATA[<h2 id="一异步入门">一、异步入门</h2>
<h4 id="11-async和await">1.1 async和await</h4>
<p>这两个关键词的本质是语法糖。例如：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> _increment() <span style="color:#66d9ef">async</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">await</span> Future.delayed(Duration(seconds: <span style="color:#ae81ff">1</span>));
</span></span><span style="display:flex;"><span>  setState((){ _counter<span style="color:#f92672">++</span>; });
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>等价于：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> _increment() {
</span></span><span style="display:flex;"><span>  Future.delayed(Duration(seconds: <span style="color:#ae81ff">1</span>),(){
</span></span><span style="display:flex;"><span>    setState((){ _counter<span style="color:#f92672">++</span>; });
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>或者：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> _increment() {
</span></span><span style="display:flex;"><span>  Future.delayed(Duration(seconds: <span style="color:#ae81ff">1</span>)).then(
</span></span><span style="display:flex;"><span>  	(value) <span style="color:#f92672">=&gt;</span> setState((){ _counter<span style="color:#f92672">++</span>; })
</span></span><span style="display:flex;"><span>  );
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>​		这里需要纠正一个误区：就像JS那样，dart的异步操作并非多线程。在dart中，每一个线程都被isolate包裹，它们之间不能共享内存，也就避免了死锁问题，降低了程序复杂度，提升GC的性能。线程之间使用消息队列来通信。</p>
<p>​		一个线程怎么处理等待事件呢？这里就要用到Event Loop机制。内置一个Event Queue和MicroTask Queue，只要MicroTask Queue中还有事件没有处理完，则先处理这里面的事件，而此时EventQueue中的事件一个都不会被执行。使用<code>scheduleMicrotask()</code>来向Microtask Queue添加事件。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main(){
</span></span><span style="display:flex;"><span>  Future(() <span style="color:#f92672">=&gt;</span> print(<span style="color:#e6db74">&#39;A&#39;</span>));
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#39;B&#39;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">//打印结果：BA
</span></span></span></code></pre></div><p>​		这里我们还是要提醒一下，只有在出现需要等待的事件时，EventLoop机制才能发挥作用。如果卡顿是因为计算量过大造成的，那么只有多开几个Isolate（线程）来解决了。</p>
<h4 id="12-运行时机">1.2 运行时机</h4>
<ol>
<li>
<p>直接运行：<code>Future.sync()</code>、<code>Future.value()</code>、<code>_.then()</code></p>
</li>
<li>
<p>Microtask：<code>scheduleMicrotask()</code>、<code>Future.microtask()</code>、<code>_completed.then()</code></p>
</li>
<li>
<p>Event：<code>Future()</code>、<code>Future.delayed()</code></p>
</li>
</ol>
<h4 id="13-运行时机举例">1.3 运行时机举例</h4>
<ol>
<li>
<p>直接运行</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main(){
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;1&#34;</span>);
</span></span><span style="display:flex;"><span>  Future.value(func());
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;3&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">String</span> func(){
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;2&#34;</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;&#34;</span>;
</span></span><span style="display:flex;"><span>}<span style="color:#75715e">//输出结果：123
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main(){
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;1&#34;</span>);
</span></span><span style="display:flex;"><span>  Future.<span style="color:#66d9ef">sync</span>(()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;2&#34;</span>));
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;3&#34;</span>);
</span></span><span style="display:flex;"><span>}<span style="color:#75715e">//输出结果：123
</span></span></span></code></pre></div></li>
<li>
<p>Event：</p>
<p>以http请求来说：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>http.<span style="color:#66d9ef">get</span>(<span style="color:#e6db74">&#39;www.baidu.com&#39;</span>)
</span></span><span style="display:flex;"><span>  .then((value)<span style="color:#f92672">=&gt;</span>{<span style="color:#66d9ef">null</span>})
</span></span><span style="display:flex;"><span>  .catchError(...);	<span style="color:#75715e">//返回一个Future
</span></span></span></code></pre></div><p>函数返回Future，这里看起来Future是立刻执行的，但实际上要在下一个循环中才会被执行：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Future<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">String</span><span style="color:#f92672">&gt;</span> getFuture(){
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> Future(() <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;alice&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main(){
</span></span><span style="display:flex;"><span>  getFuture().then((value)<span style="color:#f92672">=&gt;</span>print(value));
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;hi&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">//打印结果：hi alice
</span></span></span></code></pre></div></li>
</ol>
<p>来个大汇总吧：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main(){
</span></span><span style="display:flex;"><span>  Future.delayed(Duration(seconds: <span style="color:#ae81ff">1</span>),() <span style="color:#f92672">=&gt;</span> print(<span style="color:#e6db74">&#34;event 3&#34;</span>));
</span></span><span style="display:flex;"><span>  Future.delayed(Duration.zero, ()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;event 2&#34;</span>));
</span></span><span style="display:flex;"><span>  Future(()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;event 1&#34;</span>));<span style="color:#75715e">//这里的event 1与event 2等价，调换语句顺序，执行顺序也会对应调换
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  
</span></span><span style="display:flex;"><span>  scheduleMicrotask(()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;mtask 1&#34;</span>));
</span></span><span style="display:flex;"><span>  Future.microtask(()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;mtask 2&#34;</span>));
</span></span><span style="display:flex;"><span>  Future.value(<span style="color:#ae81ff">123</span>).then((value)<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;mtask 3&#34;</span>));	<span style="color:#75715e">//_completed.then()
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;main 1&#34;</span>);
</span></span><span style="display:flex;"><span>  Future.<span style="color:#66d9ef">sync</span>(()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;sync 1&#34;</span>));
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;main 2&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">/*
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">	输出结果：
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		mtask 1
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		mtask 2
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		mtask 3
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		event 2
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		event 1		
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		event 3
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		main 1
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		sync 1
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		main 2
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">*/</span>
</span></span></code></pre></div><p>最后落下一个会立即执行的<code>_.then()</code>：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Future.delayed(Duration(seconds: <span style="color:#ae81ff">1</span>),() <span style="color:#f92672">=&gt;</span> print(<span style="color:#e6db74">&#34;delayed&#34;</span>))
</span></span><span style="display:flex;"><span>	.then((value){
</span></span><span style="display:flex;"><span>    scheduleMicrotask(()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;micro&#34;</span>));
</span></span><span style="display:flex;"><span>    print(<span style="color:#e6db74">&#34;then&#34;</span>);
</span></span><span style="display:flex;"><span>  }).then((value)<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;then 2&#34;</span>));
</span></span><span style="display:flex;"><span><span style="color:#75715e">//结果：delayed then then2 micro
</span></span></span></code></pre></div><h4 id="14-error和whencomplete">1.4 error和whenComplete</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>getFuture()
</span></span><span style="display:flex;"><span>  .then((value)<span style="color:#f92672">=&gt;</span>print(value))
</span></span><span style="display:flex;"><span>  .catchError((err)<span style="color:#f92672">=&gt;</span>print(err))
</span></span><span style="display:flex;"><span>  .whenComplete(()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;complete&#34;</span>));
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Future<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">String</span><span style="color:#f92672">&gt;</span> getFuture(){
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> Future.error(Exception(<span style="color:#e6db74">&#34;wrong&#34;</span>));
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">//wrong complete
</span></span></span></code></pre></div><h2 id="二builder和controller">二、Builder和Controller</h2>
<h4 id="21-futurebuilder">2.1 FutureBuilder</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>body: FutureBuilder(
</span></span><span style="display:flex;"><span>	future: Future.value(<span style="color:#ae81ff">123</span>),
</span></span><span style="display:flex;"><span>  builder: (context, AsyncSnapshot<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">dynamic</span><span style="color:#f92672">&gt;</span> snapshot){
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//snapshot代表最近的状态：未完成、完成、出错
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">if</span>(snapshot.connectionState <span style="color:#f92672">==</span> ConnectionState.waiting){
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">return</span> CircularProgressIndicator();
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">else</span> <span style="color:#66d9ef">if</span>(snapshot.connectionState <span style="color:#f92672">==</span> ConnectionState.done){
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">if</span>(snapshot.hasError){
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> Text(<span style="color:#e6db74">&#34;error&#34;</span>);
</span></span><span style="display:flex;"><span>      } <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> Text(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>snapshot.data<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>);	<span style="color:#75715e">//data和error只有一个有数据
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//none代表前面设置了future: null，不可能发生；而active设计Stream，暂不考虑
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>   	<span style="color:#66d9ef">throw</span> <span style="color:#e6db74">&#34;should not happen&#34;</span>;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span><span style="color:#75715e">//更加可读
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>body: FutureBuilder(
</span></span><span style="display:flex;"><span>	future: Future.value(<span style="color:#ae81ff">123</span>),
</span></span><span style="display:flex;"><span>  initialData: <span style="color:#ae81ff">72</span>,	<span style="color:#75715e">//未完成时snapshot.data = 72
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  builder: (context, AsyncSnapshot<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">dynamic</span><span style="color:#f92672">&gt;</span> snapshot){
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span>(snapshot.hasError){
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">return</span> Icon(Icons.error, size:<span style="color:#ae81ff">80</span>);
</span></span><span style="display:flex;"><span>    } <span style="color:#66d9ef">else</span> <span style="color:#66d9ef">if</span>(snapshot.hasData){
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">return</span> Text(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>snapshot.data<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>    } <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">return</span> CircularProgressIndicator();
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>​		值得一提的是，snapshot中的error和data属性只能有一个存在值，如果出错，flutter会先删除原来data中的initialData，再填上对应的error。</p>
<h4 id="22-stream和streambuilder">2.2 Stream和StreamBuilder</h4>
<p>Future负责在以后给我们一个值，而stream则会在以后给我们很多值。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">final</span> future <span style="color:#f92672">=</span> Future.delayed(Duration(seconds: <span style="color:#ae81ff">1</span>), ()<span style="color:#f92672">=&gt;</span><span style="color:#ae81ff">42</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">final</span> stream <span style="color:#f92672">=</span> Stream.periodic(Duration(seconds: <span style="color:#ae81ff">1</span>), (_) <span style="color:#f92672">=&gt;</span> <span style="color:#ae81ff">42</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> initialState(){
</span></span><span style="display:flex;"><span>  future.then(...);
</span></span><span style="display:flex;"><span>  stream.listen((event){
</span></span><span style="display:flex;"><span>    print(<span style="color:#e6db74">&#34;stream: </span><span style="color:#e6db74">$</span>event<span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//...
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
</span></span></code></pre></div><p>可以使用StreamBuilder监控stream：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>body: StreamBuilder(
</span></span><span style="display:flex;"><span>	stream: stream,
</span></span><span style="display:flex;"><span>  builder: (context, AsyncSnapshot<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">dynamic</span><span style="color:#f92672">&gt;</span> snapshot) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//可以使用switch来判断snapshot.connectionState
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//...
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  }
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>​		Stream和Future的区别之一就在于，Future是在结束（done）的时候返回一个data或error，而Stream是在active的状态返回一个data或error，而stream一旦处于done状态，那么向stream发送数据就会引发异常。</p>
<h4 id="23-streamcontroller">2.3 StreamController</h4>
<p>形象地说，Stream就像水流，sink就像水龙头（数据来源），StreamController会同时创建这两者：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">final</span> controller <span style="color:#f92672">=</span> StreamController();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>controller.sink.add(<span style="color:#ae81ff">72</span>);<span style="color:#75715e">//添加一个值，数据类型可以混搭
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>controller.sink.addError(<span style="color:#e6db74">&#34;error&#34;</span>);<span style="color:#75715e">//添加错误
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>controller.stream.listen((event) {  });<span style="color:#75715e">//处理事件
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span>controller.close();<span style="color:#75715e">//关闭流
</span></span></span></code></pre></div><p>使用StreamBuilder：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>body: StreamBuilder(
</span></span><span style="display:flex;"><span>	stream: controller.stream,		<span style="color:#75715e">//监听controller
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  builder: (context, AsyncSnapshot<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">dynamic</span><span style="color:#f92672">&gt;</span> snapshot) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//可以使用switch来判断snapshot.connectionState
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//...
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  }
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><h4 id="24-streambroadcast">2.4 Stream.broadcast()</h4>
<p>​		一般来说，一个Stream只能被一方监听，同时未被接受的数据会被缓存；反过来说，使用<code>Stream.broadcast</code>创建的广播流可以被多方监听，但未被接受的数据不再会被缓存。</p>
<h2 id="三stream高级操作">三、Stream高级操作</h2>
<h4 id="31-streammap">3.1 Stream.map</h4>
<p>例如：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>stream: controller.stream.map((event) <span style="color:#f92672">=&gt;</span> event <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>),
</span></span></code></pre></div><p>上面的代码将会将流中的数据重复两次输出。</p>
<h4 id="32-streamwhere">3.2 Stream.where</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>stream: controller.stream.where((event) <span style="color:#f92672">=&gt;</span> event <span style="color:#66d9ef">is</span> <span style="color:#66d9ef">int</span>),
</span></span></code></pre></div><p>上面的代码只会允许int类型的数据通过。</p>
<h4 id="33-streamdistinct">3.3 Stream.distinct</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>stream: controller.stream.distinct(),
</span></span></code></pre></div><p>上面的代码将会过滤重复的值。</p>
<h4 id="34-async">3.4 async*</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Stream<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">int</span><span style="color:#f92672">&gt;</span> getNumbers() <span style="color:#66d9ef">async</span><span style="color:#f92672">*</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">while</span>(<span style="color:#66d9ef">true</span>){
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">await</span> Future.delayed(Duration(seconds: <span style="color:#ae81ff">1</span>));
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">yield</span> <span style="color:#ae81ff">1</span>;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">//StreamBuilder使用该流
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>StreamBuilder(
</span></span><span style="display:flex;"><span>	stream: getTime(),
</span></span><span style="display:flex;"><span>  builder: (...) { ... }
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>上面的代码使用<code>async*</code>关键字，每一秒钟会向流中发送一个数字。</p>
<h4 id="35-流中的状态streamtransform">3.5 流中的状态——Stream.transform</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>StreamBuilder{
</span></span><span style="display:flex;"><span>  stream: _controller.stream.transform(TallyTransformer()),
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//....
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//定义自己的TallyTransformer
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">TallyTransformer</span> <span style="color:#66d9ef">implements</span> StreamTransformer {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">int</span> sum <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>;
</span></span><span style="display:flex;"><span>  StreamController _controller <span style="color:#f92672">=</span> StreamController();
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Stream bind(Stream stream){
</span></span><span style="display:flex;"><span>    stream.listen((event) {		<span style="color:#75715e">//我们听源头的这个stream
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      sum <span style="color:#f92672">+=</span> event;
</span></span><span style="display:flex;"><span>      _controller.add(sum);
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> _controller.stream;	<span style="color:#75715e">//他人听的是这个新的stream
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  }
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  StreamTransformer<span style="color:#f92672">&lt;</span>RS, RT<span style="color:#f92672">&gt;</span> cast<span style="color:#f92672">&lt;</span>RS, RT<span style="color:#f92672">&gt;</span>() <span style="color:#f92672">=&gt;</span> 
</span></span><span style="display:flex;"><span>    StreamTransformer.castFrom(<span style="color:#66d9ef">this</span>);	<span style="color:#75715e">//类型转换
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>Flutter Scrollable教程</title>
      <link>https://widegram.com/2021/11/flutter-scrollable/</link>
      <pubDate>Mon, 08 Nov 2021 20:53:00 +0000</pubDate>
      
      <guid>https://widegram.com/2021/11/flutter-scrollable/</guid>
      <description>一、基本Scrollable 1.1 Recycle ​	类似RecyclerView，ListView会回收划出屏幕的控件，同时也会预先加载下面的几条。Flutter的回收可以使其被重复利用，也就是Recycle。缓冲区大小不是根据个数而是根据屏幕高度，大约是1/3屏幕，上下都有，可以用cacheExtent调整缓冲区高度。
1.2 分割线 ​	可以用奇偶数来做Divider()，但这不是一个很好的方法：index会偏移。可以使用ListView.seprated来做分割线。
1.3 滚动条跳转（固定高度） ​	指定Scrollbar()可以实现滚动条效果，但如果不添加itemCount属性，则列表无限长，即使滚动，滚动条也不会有反应；如果不指定itemExtent（这是一个主轴方向的紧约束），首先大幅跳转时会损失性能，其次滚动条可能会抖动。
1.4 Padding 在itemBuilder内加Padding，在视觉上会有padding，但在空白处可以滑动滚动； 在ListView.builder外加Padding，在视觉上有padding，空白处不可滑动； 设置ListView.builder内的padding属性，会在整个ListView上下留白，而不是元素间的空隙。可以用EdgeInsets.only(bottom: 200)来解决底部项目被FAB挡住的问题。 1.5 空白时显示其他控件 body: false ? Text(&amp;#34;加载中&amp;#34;) : ListView.builder 1.6 ScrollController 在ListView.builder中设置controller: _controller。例如，设置按下AppBar自动跳到顶部，可以使用以下代码：
AppBar( title: GestureDetector( onTap: () { //_controller.jumpTo(0.0); _controller.animatedTo(-20.0, duration: ,curve: ); //传入负值，出现到顶上再弹回的效果 }	//跳转 ) ) 有多个ListView时，也应设置对应的controller。
1.7 physics属性 ListView.builder的physics属性可以用来设置安卓或iOS风格的到顶回弹。例如physics: BouncingScrollPhysics()就是iOS风格的顶部回弹。
二、滑动事件 2.1 Scrollbar ​	使用iOS风格的Scrollbar可以使用CupertinoScrollbar()。同时设置isAlwaysShown和controller属性可以一直显示scrollbar。
2.2 RefreshIndicator下拉刷新 可以设置color和backgroundColor等属性设置下拉刷新指示器的样式。RefreshIndicator和Scrollbar可以互相嵌套，不要求顺序。
2.3 拦截滚动事件 使用NotificationListener，在onNotication返回true拦截事件。
2.4 Dismissible ​	在itemBuilder内返回一个Dismissible()，这里的key属性是必传的，例如UniqueKey()，设置onDismissed可以查看滑动方向并设置滑动后的操作，例如从列表中删除。
​	在Dismissible()中有background或secondaryBackground可以传入一个widget，分别对应左划右划的效果。
​	设置confirmDismiss为一个async函数，返回false表明滑动后不删除。</description>
      <content:encoded><![CDATA[<h2 id="一基本scrollable">一、基本Scrollable</h2>
<h4 id="11-recycle">1.1 Recycle</h4>
<p>​		类似RecyclerView，ListView会回收划出屏幕的控件，同时也会预先加载下面的几条。Flutter的回收可以使其被重复利用，也就是<code>Recycle</code>。缓冲区大小不是根据个数而是根据屏幕高度，大约是1/3屏幕，上下都有，可以用<code>cacheExtent</code>调整缓冲区高度。</p>
<h4 id="12-分割线">1.2 分割线</h4>
<p>​		可以用奇偶数来做<code>Divider()</code>，但这不是一个很好的方法：index会偏移。可以使用<code>ListView.seprated</code>来做分割线。</p>
<h4 id="13-滚动条跳转固定高度">1.3 滚动条跳转（固定高度）</h4>
<p>​		指定<code>Scrollbar()</code>可以实现滚动条效果，但如果不添加itemCount属性，则列表无限长，即使滚动，滚动条也不会有反应；如果不指定<code>itemExtent</code>（这是一个<strong>主轴方向</strong>的紧约束），首先大幅跳转时会损失性能，其次滚动条可能会抖动。</p>
<h4 id="14-padding">1.4 Padding</h4>
<ol>
<li>在<code>itemBuilder</code>内加<code>Padding</code>，在视觉上会有padding，但在空白处可以滑动滚动；</li>
<li>在<code>ListView.builder</code>外加<code>Padding</code>，在视觉上有padding，空白处不可滑动；</li>
<li>设置<code>ListView.builder</code>内的padding属性，会在整个<code>ListView</code>上下留白，而不是元素间的空隙。可以用<code>EdgeInsets.only(bottom: 200)</code>来解决底部项目被FAB挡住的问题。</li>
</ol>
<h4 id="15-空白时显示其他控件">1.5 空白时显示其他控件</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>body: <span style="color:#66d9ef">false</span> <span style="color:#f92672">?</span> Text(<span style="color:#e6db74">&#34;加载中&#34;</span>) <span style="color:#f92672">:</span> ListView.builder
</span></span></code></pre></div><h4 id="16-scrollcontroller">1.6 ScrollController</h4>
<p>在<code>ListView.builder</code>中设置<code>controller: _controller</code>。例如，设置按下AppBar自动跳到顶部，可以使用以下代码：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>AppBar(
</span></span><span style="display:flex;"><span>	title: GestureDetector(
</span></span><span style="display:flex;"><span>  	onTap: () {
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">//_controller.jumpTo(0.0);
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      _controller.animatedTo(<span style="color:#f92672">-</span><span style="color:#ae81ff">20.0</span>, duration: ,curve: );
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">//传入负值，出现到顶上再弹回的效果
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    }	<span style="color:#75715e">//跳转
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  )
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>有多个ListView时，也应设置对应的controller。</p>
<h4 id="17-physics属性">1.7 physics属性</h4>
<p>ListView.builder的physics属性可以用来设置安卓或iOS风格的到顶回弹。例如<code>physics: BouncingScrollPhysics()</code>就是iOS风格的顶部回弹。</p>
<h2 id="二滑动事件">二、滑动事件</h2>
<h4 id="21-scrollbar">2.1 Scrollbar</h4>
<p>​		使用iOS风格的<code>Scrollbar</code>可以使用<code>CupertinoScrollbar()</code>。同时设置<code>isAlwaysShown</code>和<code>controller</code>属性可以一直显示scrollbar。</p>
<h4 id="22-refreshindicator下拉刷新">2.2 RefreshIndicator下拉刷新</h4>
<p>可以设置<code>color</code>和<code>backgroundColor</code>等属性设置下拉刷新指示器的样式。<code>RefreshIndicator</code>和<code>Scrollbar</code>可以互相嵌套，不要求顺序。</p>
<h4 id="23-拦截滚动事件">2.3 拦截滚动事件</h4>
<p>使用<code>NotificationListener</code>，在<code>onNotication</code>返回<code>true</code>拦截事件。</p>
<h4 id="24-dismissible">2.4 Dismissible</h4>
<p>​		在itemBuilder内返回一个<code>Dismissible()</code>，这里的<code>key</code>属性是必传的，例如<code>UniqueKey()</code>，设置onDismissed可以查看滑动方向并设置滑动后的操作，例如从列表中删除。</p>
<p>​		在<code>Dismissible()</code>中有<code>background</code>或<code>secondaryBackground</code>可以传入一个<code>widget</code>，分别对应左划右划的效果。</p>
<p>​		设置<code>confirmDismiss</code>为一个<code>async</code>函数，返回<code>false</code>表明滑动后不删除。</p>
<p>​		设置<code>onResize</code>属性可以在该项消失、屏幕开始变化时不停地调用；<code>movementDuration</code>可以设置滑动的时常，movement后就onResize；<code>dismissThreshold</code>设置滑动多少后消除；<code>direction</code>可以设置滑动方向。</p>
<h2 id="三其他控件">三、其他控件</h2>
<h4 id="31-3d转轮滚动效果listwheelscrollview">3.1 3D转轮滚动效果——ListWheelScrollView</h4>
<ol>
<li>offAxisFraction：偏移数值</li>
<li>diameterRatio：直径比例</li>
<li>overAndUnderCenterOpacity：设置上下不透明度</li>
<li>magnification：放大倍数</li>
<li>physics：滚动物理效果</li>
<li>onSelectedItemChanged：选择的内容变化</li>
<li>使用<code>RotatedBox</code>来进行横向滚动</li>
</ol>
<h4 id="32-滚动页面pageview">3.2 滚动页面——PageView</h4>
<ol>
<li>pageSnapping：划到中间可以停住</li>
<li>scrollDirection：设置滚动方向</li>
<li>onPageChanged</li>
</ol>
<h4 id="33-reorderablelistview">3.3 ReorderableListView</h4>
<ol>
<li>
<p>children：List.generate(&hellip;)</p>
</li>
<li>
<p>onReorder: (int oldIndex,int newIndex)：移动后的新位置和旧位置</p>
<ol>
<li>
<p>这里有一个小坑，应该这样写：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>onReorder: (<span style="color:#66d9ef">int</span> oldIndex,<span style="color:#66d9ef">int</span> newIndex){
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span>(newIndex <span style="color:#f92672">&gt;</span> oldIndex) newIndex<span style="color:#f92672">--</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//...继续写其他的业务逻辑
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
</span></span></code></pre></div></li>
</ol>
</li>
<li>
<p>在每个ListItem中必须设置单独的key，否则会报错，因为它的排序属性。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>ReorderableListView(
</span></span><span style="display:flex;"><span>	children: List.generate(
</span></span><span style="display:flex;"><span>  	<span style="color:#ae81ff">20</span>,
</span></span><span style="display:flex;"><span>    (index) <span style="color:#f92672">=&gt;</span> Text(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">$</span>index<span style="color:#e6db74">&#34;</span>,key: UniqueKey()),
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  )
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div></li>
</ol>
<h4 id="34-singlechildscrollview">3.4 SingleChildScrollView</h4>
<p>这个组件一般不推荐用，除非是一般不需要滑动的组件，在屏幕不够大、显示不下的情况下偶尔滑动。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Flutter 布局全解</title>
      <link>https://widegram.com/2021/11/flutter-layout/</link>
      <pubDate>Wed, 03 Nov 2021 16:15:00 +0000</pubDate>
      
      <guid>https://widegram.com/2021/11/flutter-layout/</guid>
      <description>一、初识布局 1.1 约束（Terminology）的向下传递 ​	Flutter从一个runApp函数开始运行，例如：
void main() { runApp(//1.向下Container传递紧约束：屏幕尺寸 Container(//2.向下Center传递约束，由于自身的w和h无效，因此原封不动地传递 width: 0.04,//1.来自App的约束使width失效，w=页面宽度 height: 0.08,//1.来自App的约束使height失效，h=页面高度 color: Colors.red[200], child: Center(//3.向下Container传递松约束，下限设为0 child: Container(//4.向下FlutterLogo传递紧约束：200&amp;lt;=h,w&amp;lt;=200 color: Colors.white, width: 200,//3.满足Center的约束，w=200 height: 200,//3.满足Center的约束，h=200 child: FlutterLogo( size: 10//不满足约束条件，失效，size=200 ), ), ) )); } 上面这段代码的运行结果如下图：
​	通过读取LayoutBuilder的constrants参数，我们可以得到来自父组件的约束区间：
LayoutBuilder(builder: (context, constrants) { print(&amp;#34;constrants: $constrants&amp;#34;); return FlutterLogo(size: 10); }), ​	我们可以通过Align系列控件来放松约束，例如使用Center。
1.2 SizedBox和FractionallySizedBox ​	我们可以使用SizedBox和FractionallySizedBox来确定组件的大小，其中后者使用一个比例关系来约束。
​	但如果SizedBox本身受到一个紧约束，那么它也无法突破。
​	目前我们只能使用Align系列Widgets（比如Center）来将紧约束转换为松约束，例如将200&amp;lt;=h,w&amp;lt;=200转化为0&amp;lt;=h,w&amp;lt;=200。
Container( //向下SizedBox传递紧约束：200&amp;lt;=h,w&amp;lt;=200 width: 200, height: 200, child: SizedBox( //收到Container的紧约束 height: 100,//失效 width: 100,//失效 child: FlutterLogo(size: 10), //不满足约束条件，失效，size=200 ) 1.</description>
      <content:encoded><![CDATA[<h3 id="一初识布局">一、初识布局</h3>
<h4 id="11-约束terminology的向下传递">1.1 约束（Terminology）的向下传递</h4>
<p>​		Flutter从一个runApp函数开始运行，例如：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() {
</span></span><span style="display:flex;"><span>  runApp(<span style="color:#75715e">//1.向下Container传递紧约束：屏幕尺寸
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    Container(<span style="color:#75715e">//2.向下Center传递约束，由于自身的w和h无效，因此原封不动地传递
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      width: <span style="color:#ae81ff">0.04</span>,<span style="color:#75715e">//1.来自App的约束使width失效，w=页面宽度
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      height: <span style="color:#ae81ff">0.08</span>,<span style="color:#75715e">//1.来自App的约束使height失效，h=页面高度
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      color: Colors.red[<span style="color:#ae81ff">200</span>],
</span></span><span style="display:flex;"><span>      child: Center(<span style="color:#75715e">//3.向下Container传递松约束，下限设为0
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        child: Container(<span style="color:#75715e">//4.向下FlutterLogo传递紧约束：200&lt;=h,w&lt;=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          color: Colors.white,
</span></span><span style="display:flex;"><span>          width: <span style="color:#ae81ff">200</span>,<span style="color:#75715e">//3.满足Center的约束，w=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          height: <span style="color:#ae81ff">200</span>,<span style="color:#75715e">//3.满足Center的约束，h=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          child: FlutterLogo(
</span></span><span style="display:flex;"><span>            size: <span style="color:#ae81ff">10</span><span style="color:#75715e">//不满足约束条件，失效，size=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          ),
</span></span><span style="display:flex;"><span>        ),
</span></span><span style="display:flex;"><span>      )
</span></span><span style="display:flex;"><span>    ));
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>上面这段代码的运行结果如下图：</p>
<img src="https://i.loli.net/2021/11/03/pEhd8VH92mYLa6B.png" alt="截屏2021-11-03 上午10.33.40" style="zoom:25%;" />
<p>​		通过读取<code>LayoutBuilder</code>的constrants参数，我们可以得到来自父组件的约束区间：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>LayoutBuilder(builder: (context, constrants) {
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;constrants: </span><span style="color:#e6db74">$</span>constrants<span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> FlutterLogo(size: <span style="color:#ae81ff">10</span>);
</span></span><span style="display:flex;"><span>}),
</span></span></code></pre></div><p>​		我们可以通过<code>Align</code>系列控件来放松约束，例如使用<code>Center</code>。</p>
<h4 id="12-sizedbox和fractionallysizedbox">1.2 SizedBox和FractionallySizedBox</h4>
<p>​		我们可以使用<code>SizedBox</code>和<code>FractionallySizedBox</code>来确定组件的大小，其中后者使用一个比例关系来约束。</p>
<p>​		但如果<code>SizedBox</code>本身受到一个紧约束，那么它也无法突破。</p>
<p>​		目前我们只能使用<code>Align</code>系列Widgets（比如<code>Center</code>）来将紧约束转换为松约束，例如将<code>200&lt;=h,w&lt;=200</code>转化为<code>0&lt;=h,w&lt;=200</code>。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//向下SizedBox传递紧约束：200&lt;=h,w&lt;=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  width: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  height: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  child: SizedBox(  <span style="color:#75715e">//收到Container的紧约束
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    height: <span style="color:#ae81ff">100</span>,<span style="color:#75715e">//失效
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    width: <span style="color:#ae81ff">100</span>,<span style="color:#75715e">//失效
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child: FlutterLogo(size: <span style="color:#ae81ff">10</span>), <span style="color:#75715e">//不满足约束条件，失效，size=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  )
</span></span></code></pre></div><h4 id="13-constrainedbox">1.3 ConstrainedBox</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container( <span style="color:#75715e">//向下传递紧约束：h,w=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  color: Colors.black,
</span></span><span style="display:flex;"><span>  width: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  height: <span style="color:#ae81ff">500</span>,
</span></span><span style="display:flex;"><span>  child: Center(<span style="color:#75715e">//向下传递松约束：0&lt;=h,w&lt;=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child: ConstrainedBox(<span style="color:#75715e">//重新定义约束区间（同样，收到上级约束的限制）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      constraints: <span style="color:#66d9ef">const</span> BoxConstraints(
</span></span><span style="display:flex;"><span>        minWidth: <span style="color:#ae81ff">60</span>,
</span></span><span style="display:flex;"><span>        maxWidth: <span style="color:#66d9ef">double</span>.infinity,<span style="color:#75715e">//无限大，被上级限制为200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        minHeight: <span style="color:#ae81ff">60</span>,
</span></span><span style="display:flex;"><span>        maxHeight: <span style="color:#ae81ff">120</span>,
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>      child: LayoutBuilder(builder: (context, constraints) {
</span></span><span style="display:flex;"><span>        print(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">$</span>constraints<span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//flutter: BoxConstraints(60.0&lt;=w&lt;=120.0, 60.0&lt;=h&lt;=120.0)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">const</span> FlutterLogo(size: <span style="color:#ae81ff">150</span>);<span style="color:#75715e">//150被限制回120
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      }),
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><p>​		<code>ConstrainedBox</code>可以重新定义约束区间，但新定义的约束区间必须在父约束之内。我们可以使用<code>loosen()</code>函数取消<code>minWidth</code>和<code>minHeight</code>，也就是将它们设置为0.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container( <span style="color:#75715e">//向下传递紧约束：h,w=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  color: Colors.black,
</span></span><span style="display:flex;"><span>  width: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  height: <span style="color:#ae81ff">500</span>,
</span></span><span style="display:flex;"><span>  child: Center(<span style="color:#75715e">//向下传递松约束：0&lt;=h,w&lt;=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child: ConstrainedBox(<span style="color:#75715e">//重新定义约束区间（同样，收到上级约束的限制）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      constraints: <span style="color:#66d9ef">const</span> BoxConstraints(
</span></span><span style="display:flex;"><span>        minWidth: <span style="color:#ae81ff">60</span>,
</span></span><span style="display:flex;"><span>        maxWidth: <span style="color:#66d9ef">double</span>.infinity,<span style="color:#75715e">//无限大，被上级限制为200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        minHeight: <span style="color:#ae81ff">60</span>,
</span></span><span style="display:flex;"><span>        maxHeight: <span style="color:#ae81ff">120</span>,
</span></span><span style="display:flex;"><span>      ).loosen(),	<span style="color:#75715e">//变为松约束（下限为0）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      child: LayoutBuilder(builder: (context, constraints) {
</span></span><span style="display:flex;"><span>        print(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">$</span>constraints<span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//flutter: BoxConstraints(0.0&lt;=w&lt;=120.0, 0.0&lt;=h&lt;=120.0)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">const</span> FlutterLogo(size: <span style="color:#ae81ff">150</span>);<span style="color:#75715e">//150被限制回120
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      }),
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><ul>
<li>紧约束：最大值等于最小值的约束；</li>
<li>松约束：最小值等于0的约束；</li>
<li>既是松约束，又是紧约束的约束：最大值与最小值都是0的约束。</li>
</ul>
<p>同时，</p>
<ul>
<li>有界约束：有设置最大值的约束；</li>
<li>无界约束：最大值是无限大的约束，例如无限长的ListView。</li>
</ul>
<h4 id="14-padding">1.4 Padding</h4>
<p>Padding组件可以收缩Container的紧约束，但仍向下传递一个紧约束。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.black,
</span></span><span style="display:flex;"><span>  width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>  height: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>  child: Padding(
</span></span><span style="display:flex;"><span>    padding: <span style="color:#66d9ef">const</span> EdgeInsets.all(<span style="color:#ae81ff">20.0</span>),
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//上下同时设置20的边框，收缩了Container的紧约束
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child: LayoutBuilder(builder: (context, constraints) {
</span></span><span style="display:flex;"><span>      print(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">$</span>constraints<span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">//BoxConstraints(w=260.0, h=260.0)，这里仍然是一个紧约束
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">const</span> FlutterLogo(size: <span style="color:#ae81ff">150</span>);<span style="color:#75715e">//150仍然失效
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    }),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><h3 id="二弹性布局column和flexible">二、弹性布局——Column和Flexible</h3>
<h4 id="21-column和directionality">2.1 Column和Directionality</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Scaffold(
</span></span><span style="display:flex;"><span>  body: Center(
</span></span><span style="display:flex;"><span>    child: Container(
</span></span><span style="display:flex;"><span>      color: Colors.red[<span style="color:#ae81ff">200</span>],
</span></span><span style="display:flex;"><span>      width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>      child: Column(
</span></span><span style="display:flex;"><span>        mainAxisAlignment: MainAxisAlignment.spaceAround,
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//主轴环绕留白
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        crossAxisAlignment: CrossAxisAlignment.start,
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//交叉轴：从起始处（一般从左到右）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        <span style="color:#75715e">//某些语言的阅读顺序是从右向左，可以使用Directionality调整
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        children: <span style="color:#66d9ef">const</span> [
</span></span><span style="display:flex;"><span>          FlutterLogo(size:<span style="color:#ae81ff">150</span>),
</span></span><span style="display:flex;"><span>          FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>          FlutterLogo(size: <span style="color:#ae81ff">150</span>),
</span></span><span style="display:flex;"><span>        ],)),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/Gh1aBpITdjw5lvA.png" alt="截屏2021-11-03 上午11.34.26" style="zoom:25%;" />
<p>Column还有一些其他属性，如<code>mainAxisSize</code>和<code>CrossAxisAlignment.stretch</code>：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Scaffold(
</span></span><span style="display:flex;"><span>  body: Center(
</span></span><span style="display:flex;"><span>    child: Container(
</span></span><span style="display:flex;"><span>      color: Colors.red[<span style="color:#ae81ff">200</span>],
</span></span><span style="display:flex;"><span>      child: Column(
</span></span><span style="display:flex;"><span>        mainAxisSize: MainAxisSize.min,<span style="color:#75715e">//设置主轴方向的长度，默认max
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        mainAxisAlignment: MainAxisAlignment.spaceAround,
</span></span><span style="display:flex;"><span>        crossAxisAlignment: CrossAxisAlignment.stretch,
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//交叉轴：拉伸至与父限制一致（类型交叉轴的expanded）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        children: <span style="color:#66d9ef">const</span> [
</span></span><span style="display:flex;"><span>          FlutterLogo(size:<span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>          FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>          FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>        ],)),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/RFAs3YZqIyvbn7g.png" alt="截屏2021-11-03 上午11.38.30" style="zoom:25%;" />
<h4 id="22-expanded">2.2 Expanded</h4>
<p>使用<code>Expanded</code>组件尽可能多地填满剩余空间：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Center(
</span></span><span style="display:flex;"><span>  child: Container(
</span></span><span style="display:flex;"><span>    color: Colors.red[<span style="color:#ae81ff">200</span>],
</span></span><span style="display:flex;"><span>    child: Column(
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">//交叉轴：拉伸至与父限制一致（类型交叉轴的expand）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      children: [
</span></span><span style="display:flex;"><span>        FlutterLogo(size:<span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>        FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>        Expanded(child: Container(color: Colors.black)),
</span></span><span style="display:flex;"><span>        FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>      ],)),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/eksSJ6NKMBdh4oL.png" style="zoom:25%;" />
<p>通过指定<code>flex</code>参数设置两个<code>Expanded</code>之间的比例：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Center(
</span></span><span style="display:flex;"><span>  child: Container(
</span></span><span style="display:flex;"><span>    color: Colors.red[<span style="color:#ae81ff">200</span>],
</span></span><span style="display:flex;"><span>    child: Column(
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">//交叉轴：拉伸至与父限制一致（类型交叉轴的expand）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      children: [
</span></span><span style="display:flex;"><span>        FlutterLogo(size:<span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>        Expanded(flex:<span style="color:#ae81ff">1</span>, child: Container(color: Colors.green)),
</span></span><span style="display:flex;"><span>        FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>        Expanded(flex:<span style="color:#ae81ff">2</span>, child: Container(color: Colors.green)),
</span></span><span style="display:flex;"><span>        FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>      ],)),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/JMw7WFI8U2kabcB.png" alt="截屏2021-11-03 上午11.46.01" style="zoom:25%;" />
<p><code>Expanded</code>组件的本质是<code>Flexible</code>组件，例如：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Expanded(
</span></span><span style="display:flex;"><span>  child: Container(color: Colors.green)
</span></span><span style="display:flex;"><span>),
</span></span><span style="display:flex;"><span><span style="color:#75715e">//等价于
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>Flexible(
</span></span><span style="display:flex;"><span>  flex:<span style="color:#ae81ff">1</span>,
</span></span><span style="display:flex;"><span>  child: Container(color: Colors.green)
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><ul>
<li>
<p>使用Flexible时，Flutter首先会渲染Column中固定的项目，再用剩余的空间分比例占取。</p>
</li>
<li>
<p>Flutter在布局其中的非弹性控件时（例如FlutterLogo），会先假装自己是一个无界约束，例如</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Column(
</span></span><span style="display:flex;"><span>  children: [
</span></span><span style="display:flex;"><span>    LayoutBuilder(builder: (_, con) {
</span></span><span style="display:flex;"><span>      print(<span style="color:#e6db74">&#39;</span><span style="color:#e6db74">$</span>con<span style="color:#e6db74">&#39;</span>);
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">//flutter: BoxConstraints(0.0&lt;=w&lt;=375.0, 0.0&lt;=h&lt;=Infinity)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      <span style="color:#66d9ef">return</span> FlutterLogo(size: <span style="color:#ae81ff">50</span>);
</span></span><span style="display:flex;"><span>    }),
</span></span><span style="display:flex;"><span>    Expanded(flex: <span style="color:#ae81ff">1</span>, child: Container(color: Colors.green)),
</span></span><span style="display:flex;"><span>    FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>    Expanded(flex: <span style="color:#ae81ff">2</span>, child: Container(color: Colors.green)),
</span></span><span style="display:flex;"><span>    FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>这是由于Column没有办法将自己受到的限制提前告知FlutterLogo，为了尽可能地不限制FlutterLogo，Column会传入一个Infinity“假装”自己是一个无界控件，但如果真的超出了限制，则会提示错误。</p>
</li>
</ul>
<h4 id="23-column中的listview">2.3 Column中的ListView</h4>
<p>倘若我们直接在Column中插入ListView，则会提示错误：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Column(
</span></span><span style="display:flex;"><span>  children: [
</span></span><span style="display:flex;"><span>    FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>    ListView(children: [	<span style="color:#75715e">//报错！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      <span style="color:#66d9ef">for</span>(<span style="color:#66d9ef">int</span> i<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>;i<span style="color:#f92672">&lt;</span><span style="color:#ae81ff">10</span>;i<span style="color:#f92672">++</span>) Text(<span style="color:#e6db74">&#34;ListView item </span><span style="color:#e6db74">$</span>i<span style="color:#e6db74">&#34;</span>)
</span></span><span style="display:flex;"><span>    ]),
</span></span><span style="display:flex;"><span>    FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>正如我们上面提到的那样，Column在渲染时会假装自己是一个无界控件。Column的渲染分成两个阶段：</p>
<ol>
<li>
<p>渲染固定组件（非<code>Flexible</code>组件）</p>
</li>
<li>
<p>将剩余的空间分配给children中的各个弹性（也就是<code>Flexible</code>）组件</p>
</li>
</ol>
<p>​		而在上面的代码中，<code>ListView</code>不是一个Flexible组件，所以它参与第一个阶段的渲染，它发现自己受到的约束是Infinity（请参考2.2节的最后一段代码）。而ListView本身也是一个Infinity组件，于是它就真的使用Infinity的空间，超出了Column实际受到的限制（比如：屏幕尺寸），于是就会产生内容溢出的错误。</p>
<p>​		因此，如果我们需要在Column中使用ListView，就需要给予ListView限制，可以用Expanded包裹ListView：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Column(
</span></span><span style="display:flex;"><span>  children: [
</span></span><span style="display:flex;"><span>    FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>    Expanded(	<span style="color:#75715e">//使用Expanded包裹ListView
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      child: ListView(children: [
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span>(<span style="color:#66d9ef">int</span> i<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>;i<span style="color:#f92672">&lt;</span><span style="color:#ae81ff">100</span>;i<span style="color:#f92672">++</span>) Text(<span style="color:#e6db74">&#34;ListView item </span><span style="color:#e6db74">$</span>i<span style="color:#e6db74">&#34;</span>)
</span></span><span style="display:flex;"><span>      ]),
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>    FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/X4p1wmiqUKI62yh.png" alt="截屏2021-11-03 下午12.12.23" style="zoom:25%;" />
<h3 id="三层叠组件stack">三、层叠组件——Stack</h3>
<h4 id="31-stack组件">3.1 Stack组件</h4>
<p>用下面一段代码展示Stack的作用：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Center(
</span></span><span style="display:flex;"><span>  child: Container(
</span></span><span style="display:flex;"><span>    color: Colors.grey, <span style="color:#75715e">//可以看到Stack本身的大小，受到children的影响
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child: Stack(
</span></span><span style="display:flex;"><span>      alignment: Alignment(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">0</span>),<span style="color:#75715e">//等价于Alignment.center
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      children: [
</span></span><span style="display:flex;"><span>        FlutterLogo(size: <span style="color:#ae81ff">100</span>),
</span></span><span style="display:flex;"><span>        Text(<span style="color:#e6db74">&#34;111&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">150</span>)),
</span></span><span style="display:flex;"><span>        Positioned(
</span></span><span style="display:flex;"><span>          left: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>          top: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>          child: Container(color: Colors.red,width: <span style="color:#ae81ff">30</span>,height: <span style="color:#ae81ff">30</span>)
</span></span><span style="display:flex;"><span>        ),
</span></span><span style="display:flex;"><span>      ],),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/y8FeoVPAhQ2UYjT.png" style="zoom:25%;" />
<h4 id="32-stack的渲染过程">3.2 Stack的渲染过程</h4>
<p>​		与Column组件类似，Stack中的成员分成两类：有位置的（也就是<code>Positioned</code>）和没有位置的。</p>
<p>​		在渲染的过程中Stack如何确定其自身的大小呢？</p>
<ol>
<li>Stack会首先将没有位置的组件全部渲染出来，并以此确定其自身的大小；</li>
<li>然后再按照位置渲染<code>Positioned</code>组件。</li>
</ol>
<p>​		我们再看一个例子：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.grey,
</span></span><span style="display:flex;"><span>  child: Stack(
</span></span><span style="display:flex;"><span>    alignment: Alignment(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">0</span>),
</span></span><span style="display:flex;"><span>    children: [
</span></span><span style="display:flex;"><span>      Positioned(	<span style="color:#75715e">//Stack本身的大小与Positioned组件无关！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        left: <span style="color:#ae81ff">0</span>, bottom: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        child: FlutterLogo(size: <span style="color:#ae81ff">300</span>)
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>      Text(<span style="color:#e6db74">&#34;111&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">150</span>)),
</span></span><span style="display:flex;"><span>      Positioned(
</span></span><span style="display:flex;"><span>        left: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        top: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        child: Container(color: Colors.red,width: <span style="color:#ae81ff">30</span>,height: <span style="color:#ae81ff">30</span>)
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ],),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>也因此，FlutterLogo没有被完整渲染出来：</p>
<p>​	<img src="https://i.loli.net/2021/11/03/zSFwXPbdurvoVOB.png" style="zoom:25%;" /></p>
<p>​		我们可以指定<code>fit属性</code>来将父组件的约束传给下级组件。不是让<code>Stack</code>本身扩展，而是让其中的<code>children</code>受到<code>Stack</code>上级组件的约束，<code>Stack</code>一般也会受到影响，但那只是一个副作用：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Stack(
</span></span><span style="display:flex;"><span>  fit: StackFit.expand,
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>	constraints: BoxConstraints(
</span></span><span style="display:flex;"><span>  	minWidth:<span style="color:#ae81ff">30</span>,
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>  child: Stack(
</span></span><span style="display:flex;"><span>  	fit: StackFit.passthrough,
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//直接将上级的非紧约束传给children
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  )
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><h4 id="33-positioned组件">3.3 Positioned组件</h4>
<p>首先，Positioned会强行要求其中的位置信息达成，也就是间接地确定了其中控件的大小：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.grey,
</span></span><span style="display:flex;"><span>  child: Stack(
</span></span><span style="display:flex;"><span>    alignment: Alignment(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">0</span>),
</span></span><span style="display:flex;"><span>    children: [
</span></span><span style="display:flex;"><span>      Positioned(
</span></span><span style="display:flex;"><span>        left: <span style="color:#ae81ff">0</span>,	<span style="color:#75715e">//假如我们既设置了left，又设置了right，会怎么样呢？
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        right: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        child: Container(color: Colors.blue, width: <span style="color:#ae81ff">30</span>, height: <span style="color:#ae81ff">30</span>)
</span></span><span style="display:flex;"><span>        		<span style="color:#75715e">//答案是：Container本身的w和h将会失效！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      ),
</span></span><span style="display:flex;"><span>      Text(<span style="color:#e6db74">&#34;111&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">150</span>)),
</span></span><span style="display:flex;"><span>      Positioned(
</span></span><span style="display:flex;"><span>        left: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        top: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        child: Container(color: Colors.red,width: <span style="color:#ae81ff">30</span>,height: <span style="color:#ae81ff">30</span>)
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ],),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/3EeR86ctANnlgHp.png" alt="截屏2021-11-03 下午12.43.16" style="zoom:25%;" />
<p>​		另外，如果我们定义了Positioned，但却没有给它传递任何信息，那么Stack就会将Positioned忽略，此时Positioned里面的组件将会参与第一阶段的渲染，也就是会影响Stack本身的大小。</p>
<p>​		我们可以通过指定<code>clipBehavior</code>属性来确定超出部分是否会被裁减：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.grey, <span style="color:#75715e">//可以看到Stack本身的大小，受到children的影响
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  child: Stack(
</span></span><span style="display:flex;"><span>    clipBehavior: Clip.none,<span style="color:#75715e">//默认Clip.hardEdge
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//等价于旧版本的overflow: Overflow.visible,
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    alignment: Alignment(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">0</span>),<span style="color:#75715e">//等价于Alignment.center
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    children: [
</span></span><span style="display:flex;"><span>      Positioned(
</span></span><span style="display:flex;"><span>        left: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        width: <span style="color:#ae81ff">300</span>, <span style="color:#75715e">//溢出边框啦！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        child: Container(color: Colors.blue, width: <span style="color:#ae81ff">30</span>, height: <span style="color:#ae81ff">30</span>)
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>      Text(<span style="color:#e6db74">&#34;111&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">150</span>)),
</span></span><span style="display:flex;"><span>      Positioned(
</span></span><span style="display:flex;"><span>        left: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        top: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        child: Container(color: Colors.red,width: <span style="color:#ae81ff">30</span>,height: <span style="color:#ae81ff">30</span>)
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ],),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/VTzH7MPkRSw8csg.png" alt="截屏2021-11-03 下午12.52.04" style="zoom:25%;" />
<p>​		需要注意的是，如果控件的尺寸超出了Stack本身的尺寸，那么在Stack外的点击事件将不会生效。因为那里的点击事件不会发送给Stack，那么也就不会传送给Stack的children。</p>
<p>​		另外，即使设置了<code>Clip.hardEdge</code>，对于Transform指定的变换位移，超出部分依然不会被裁减，这也是因为Flutter的优化机制导致的。</p>
<h3 id="四再谈container">四、再谈Container</h3>
<h4 id="41-container语法糖">4.1 Container语法糖</h4>
<p>​		一般来说，Container会传递一个紧约束，我们可以通过Align系列组件放松约束：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//很繁琐
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.orange,
</span></span><span style="display:flex;"><span>  width: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  height: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  child: <span style="color:#66d9ef">const</span> Padding(
</span></span><span style="display:flex;"><span>    padding: EdgeInsets.all(<span style="color:#ae81ff">24.0</span>),
</span></span><span style="display:flex;"><span>    child: Align(
</span></span><span style="display:flex;"><span>      alignment: Alignment.topLeft,
</span></span><span style="display:flex;"><span>      child: FlutterLogo(),
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>​		这样做实在太繁琐，我们可以直接使用Container中的<code>padding</code>和<code>alignment</code>属性，这是Flutter提供的语法糖：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//与上面的代码等价
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.orange,
</span></span><span style="display:flex;"><span>  width: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  height: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  padding: <span style="color:#66d9ef">const</span> EdgeInsets.all(<span style="color:#ae81ff">24.0</span>),
</span></span><span style="display:flex;"><span>  alignment: Alignment.topLeft,
</span></span><span style="display:flex;"><span>  child: <span style="color:#66d9ef">const</span> FlutterLogo(),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>另外，还有一些其他的语法糖，例如<code>color</code>、<code>decoration</code>、<code>foregroundDecoration</code>、<code>constraints</code>对应<code>ColoredBox</code>、<code>DecoratedBox</code>、<code>ConstrainedBox</code>，<code>height/width</code>对应<code>SizedBox</code>。</p>
<ul>
<li>color =&gt; ColoredBox</li>
<li>decoration / foregroundDecoration =&gt; DecoratedBox</li>
<li>constraints =&gt; ConstrainedBox</li>
<li>clipBehavior =&gt; Clip</li>
<li>height/width =&gt; SizedBox</li>
<li>margin =&gt; Padding 添加在Container外</li>
<li>padding =&gt; Padding 添加在child外，Container内</li>
<li>transform =&gt; Transform组件</li>
</ul>
<h4 id="42-container行为">4.2 Container行为</h4>
<p>​		一般来说，如果给Container指定一个child，那么Container的大小就会与其child一致，除非您设置了alignment属性。否则就会直接占满父组件。</p>
<p>​		而如果Container在诸如Column这样主轴无界的组件内，那么它的该轴上的长度会被调整为0。</p>
<p>​		使用margin属性时也会影响Container的大小。</p>
<p>​		<code>Container</code>如何知道自己所在的组件是否有界呢？这里可以参考<code>LimitedBox</code>组件，当出现某个尺寸无穷大的情况，会自动缩减成<code>maxHeight</code>，而如果不是infinity的情况，该轴的尺寸则可以超过<code>maxHeight</code>，也就是<code>LimitedBox</code>此时无任何效果，因此，这里的<code>maxHeight</code>其实可以等同于<code>Placeholder</code>中的<code>fallbackHeight</code>属性。</p>
<p>​		更多信息，可以参考Container的源代码。</p>
<p>​</p>
<h3 id="五custommultichildlayout">五、CustomMultiChildLayout</h3>
<h4 id="51-制作一个自己的stack">5.1 制作一个自己的Stack</h4>
<p>我们可以使用<code>CustomMultiChildLayout</code>组件自制一个简易的Stack：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>CustomMultiChildLayout(
</span></span><span style="display:flex;"><span>  delegate: MyDelegate(),
</span></span><span style="display:flex;"><span>  children: [
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//标记其中的组件，使得MyDelegate中可以找到这些组件
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//LayoutID与Positioned类似，只能在这里使用
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//* 这里的Z-Order由children顺序决定，与MyDelegate无关！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    LayoutId(id: <span style="color:#ae81ff">1</span>, child: FlutterLogo()),
</span></span><span style="display:flex;"><span>    LayoutId(id: <span style="color:#ae81ff">2</span>, child: FlutterLogo()),
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>自定义一个MyDelegate类作为代理：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyDelegate</span> <span style="color:#66d9ef">extends</span> MultiChildLayoutDelegate {
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> performLayout(Size size) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (hasChild(<span style="color:#ae81ff">1</span>)) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">final</span> size1 <span style="color:#f92672">=</span> layoutChild(
</span></span><span style="display:flex;"><span>          <span style="color:#ae81ff">1</span>, <span style="color:#75715e">//1号组件layout，并得到它的size（会想：向下传递限制，向上传递大小）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          BoxConstraints.tight(<span style="color:#66d9ef">const</span> Size(<span style="color:#ae81ff">100</span>, <span style="color:#ae81ff">100</span>)) <span style="color:#75715e">//向下传递限制
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          );
</span></span><span style="display:flex;"><span>      positionChild(<span style="color:#ae81ff">1</span>, Offset.zero);  <span style="color:#75715e">//将1号组件放在左上角
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (hasChild(<span style="color:#ae81ff">2</span>)) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">final</span> size2 <span style="color:#f92672">=</span> layoutChild(
</span></span><span style="display:flex;"><span>          <span style="color:#ae81ff">2</span>, <span style="color:#75715e">//2号组件layout，并得到它的size（会想：向下传递限制，向上传递大小）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          BoxConstraints.tight(<span style="color:#66d9ef">const</span> Size(<span style="color:#ae81ff">200</span>, <span style="color:#ae81ff">200</span>)) <span style="color:#75715e">//向下传递限制
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          );
</span></span><span style="display:flex;"><span>      positionChild(<span style="color:#ae81ff">2</span>, Offset.zero); <span style="color:#75715e">//将2号组件放在左上角
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//返回true表示永远需要重新更新，这个函数用作优化，这里暂不考虑
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">bool</span> shouldRelayout(_) <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/6nw7UWOLHYAGJR2.png" alt="截屏2021-11-03 下午3.28.34" style="zoom:25%;" />
#### 5.2 简易Column
我们可以通过类似方法自制一个简易的Column：
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>positionChild(<span style="color:#ae81ff">2</span>, Offset(<span style="color:#ae81ff">0</span>,size1.height)); <span style="color:#75715e">//将2号组件放在1号组件下面：类似Column
</span></span></span></code></pre></div><p>同样，我们可以设置<code>BoxConstraints.loost(size)</code>来传递松约束：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> performLayout(Size size) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (hasChild(<span style="color:#ae81ff">1</span>)) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">final</span> size1 <span style="color:#f92672">=</span> layoutChild(
</span></span><span style="display:flex;"><span>      <span style="color:#ae81ff">1</span>, <span style="color:#75715e">//1号组件layout，并得到它的size（会想：向下传递限制，向上传递大小）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      BoxConstraints.loost(size) <span style="color:#75715e">//向下传递限制
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    );
</span></span><span style="display:flex;"><span>    positionChild(<span style="color:#ae81ff">1</span>, Offset.zero);  <span style="color:#75715e">//将1号组件放在左上角
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (hasChild(<span style="color:#ae81ff">2</span>)) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">final</span> size2 <span style="color:#f92672">=</span> layoutChild(
</span></span><span style="display:flex;"><span>      <span style="color:#ae81ff">2</span>, <span style="color:#75715e">//2号组件layout，并得到它的size（会想：向下传递限制，向上传递大小）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      BoxConstraints.tight(<span style="color:#66d9ef">const</span> Size(<span style="color:#ae81ff">200</span>, <span style="color:#ae81ff">200</span>)) <span style="color:#75715e">//向下传递限制
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    );
</span></span><span style="display:flex;"><span>    positionChild(<span style="color:#ae81ff">2</span>, Offset.zero); <span style="color:#75715e">//将2号组件放在左上角
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h4 id="53-mydelegate的局限性">5.3 MyDelegate的局限性</h4>
<ol>
<li>首先，MyDelegate无法在绘制时获得其子组件的大小。通过继承<code>MultiChildLayoutDelegate</code>的<code>Size getSize(BoxConstraints c)</code>方法，我们可以得到上级组件给我们的限制，也可以向上传递我们需要的Size大小，但我们并不能通过这个函数获得子组件的大小，也就是说我们无法使得MyDelegate自适应子组件的大小。</li>
<li>我们无法在<code>performLayout</code>函数中“无中生有”，我们只能在其中画出children数组中已经存在的组件，不能在这里创造出一个新的组件。</li>
</ol>
<p>为了解决上述两个问题，我们可以使用RenderObject</p>
<h3 id="六布局原理renderobject">六、布局原理——RenderObject</h3>
<h4 id="61-简述">6.1 简述</h4>
<p>​		我们知道，平常我们在写的Stateless、Stateful组件，其实并没有将自己画在屏幕上的能力，它们只是定义了一个build方法而已。真正将内容写在屏幕上的，其实是<code>RenderObject</code>。</p>
<p>​		我们举一个例子来说明：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.grey,
</span></span><span style="display:flex;"><span>  child: MyRenderBox(
</span></span><span style="display:flex;"><span>    child: <span style="color:#66d9ef">const</span> FlutterLogo(size: <span style="color:#ae81ff">200</span>),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyRenderBox</span> <span style="color:#66d9ef">extends</span> SingleChildRenderObjectWidget {
</span></span><span style="display:flex;"><span>  MyRenderBox({<span style="color:#66d9ef">required</span> Widget child}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(child: child);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  RenderObject createRenderObject(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> RenderMyRenderBox();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">RenderMyRenderBox</span> <span style="color:#66d9ef">extends</span> RenderBox <span style="color:#66d9ef">with</span> RenderObjectWithChildMixin {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//渲染pipeline：performLayout、paint、composition
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> performLayout() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//处理布局
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child<span style="color:#f92672">!</span>.layout(constraints, <span style="color:#75715e">//也可以自定义紧约束
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        parentUsesSize: <span style="color:#66d9ef">true</span>); <span style="color:#75715e">//作为父组件需要使用子组件的size，默认是false
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>            <span style="color:#75715e">//优化用：如果不需要，子组件改动时就可以不必继续遍历渲染父组件
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//size = const Size(300, 300);
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    size <span style="color:#f92672">=</span> (child <span style="color:#f92672">as</span> RenderBox).size;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> paint(PaintingContext context, Offset offset) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//处理绘制
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    context.paintChild(child<span style="color:#f92672">!</span>, offset <span style="color:#f92672">+</span> Offset(<span style="color:#ae81ff">120</span>,<span style="color:#ae81ff">120</span>));
</span></span><span style="display:flex;"><span>    	<span style="color:#75715e">//FlutterLogo跑到MyRenderBox外面去了！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//context.canvas.draw** //可以在这里画任意图形！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//context.pushOpacity		//Opacity原理：使用push增加一个图层（资源耗费多）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/EnkvuWcwbIVN9Tz.png" alt="截屏2021-11-03 下午4.39.08" style="zoom:25%;" />
<p>​		在上述代码中，performLayout和paint是两次不同的遍历过程（flutter一般是深度优先），因此这里的FlutterLogo可以跑到MyRenderBox外面去！</p>
<p>​		paintChild函数是非常自由的，我们可以选择canvas.draw或者push系列函数做更多想要的事情，也可以干脆不做任何事。</p>
<h4 id="62-hot-reload原理updaterenderobject">6.2 Hot Reload原理——updateRenderObject</h4>
<p>通过自制一个ShadowRenderBox，我们可以实现阴影效果：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.grey,
</span></span><span style="display:flex;"><span>  child: ShadowBox(
</span></span><span style="display:flex;"><span>    distance: <span style="color:#ae81ff">20.0</span>,
</span></span><span style="display:flex;"><span>    child: <span style="color:#66d9ef">const</span> Icon(Icons.category, size: <span style="color:#ae81ff">80</span>),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ShadowBox</span> <span style="color:#66d9ef">extends</span> SingleChildRenderObjectWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">double</span> distance;
</span></span><span style="display:flex;"><span>  ShadowBox({<span style="color:#66d9ef">required</span> Widget child, <span style="color:#66d9ef">required</span> <span style="color:#66d9ef">this</span>.distance})
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(child: child);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  RenderObject createRenderObject(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> RenderShadowBox(distance: distance);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> updateRenderObject(
</span></span><span style="display:flex;"><span>      BuildContext context, covariant RenderShadowBox renderObject) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//如果不定义这个函数，热重载时会根据key和类型判断元素是否改变，如果没有改变，则继续使用原来的renderObject，distance的值不会改变
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    renderObject.distance <span style="color:#f92672">=</span> distance;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">/*
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">	也可以使用 class RenderShadowBox extends RenderProxyBox
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">	此时，不再performLayout()和paint函数不再必须，只需要写我们需要的那个函数即可
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">*/</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">RenderShadowBox</span> <span style="color:#66d9ef">extends</span> RenderBox <span style="color:#66d9ef">with</span> RenderObjectWithChildMixin {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">double</span> distance;
</span></span><span style="display:flex;"><span>  RenderShadowBox({<span style="color:#66d9ef">required</span> <span style="color:#66d9ef">this</span>.distance});
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> performLayout() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//处理布局
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child<span style="color:#f92672">!</span>.layout(constraints, parentUsesSize: <span style="color:#66d9ef">true</span>);
</span></span><span style="display:flex;"><span>    size <span style="color:#f92672">=</span> (child <span style="color:#f92672">as</span> RenderBox).size;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> paint(PaintingContext context, Offset offset) {
</span></span><span style="display:flex;"><span>    context.paintChild(child<span style="color:#f92672">!</span>, offset);
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//插入一个新的半透明图层
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    context.pushOpacity(offset, <span style="color:#ae81ff">127</span>, (context, offset) {
</span></span><span style="display:flex;"><span>      context.paintChild(child<span style="color:#f92672">!</span>, offset <span style="color:#f92672">+</span> Offset(distance, distance));
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/4K9Utaw3AQTY7fx.png" alt="截屏2021-11-03 下午5.01.29" style="zoom:25%;" />
<p>上面的<code>RenderShadowBox</code>可以简单地写成下面的形式：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">RenderShadowBox</span> <span style="color:#66d9ef">extends</span> RenderProxyBox {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">double</span> distance;
</span></span><span style="display:flex;"><span>  RenderShadowBox({<span style="color:#66d9ef">required</span> <span style="color:#66d9ef">this</span>.distance});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> paint(PaintingContext context, Offset offset) {
</span></span><span style="display:flex;"><span>    context.paintChild(child<span style="color:#f92672">!</span>, offset);
</span></span><span style="display:flex;"><span>    context.pushOpacity(offset, <span style="color:#ae81ff">127</span>, (context, offset) {
</span></span><span style="display:flex;"><span>      context.paintChild(child<span style="color:#f92672">!</span>, offset <span style="color:#f92672">+</span> Offset(distance, distance));
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>​		另外，也可以设置<code>DebugOverflowIndicatorMixin</code>来定义超出界限后的那个黑黄相间的错误框。那个overflow组件也只是通过<code>paintOverflowIndicator()</code>手动绘制的而已。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Flutter GridView简介</title>
      <link>https://widegram.com/2021/11/flutter-gridview/</link>
      <pubDate>Wed, 03 Nov 2021 01:52:00 +0000</pubDate>
      
      <guid>https://widegram.com/2021/11/flutter-gridview/</guid>
      <description>GridView import &amp;#39;package:flutter/material.dart&amp;#39;; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: &amp;#39;Flutter Demo&amp;#39;, theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomePage(title: &amp;#39;Flutter Demo Home Page&amp;#39;), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({Key? key, required this.title}) : super(key: key); final String title; @override State&amp;lt;MyHomePage&amp;gt; createState() =&amp;gt; _MyHomePageState(); } class _MyHomePageState extends State&amp;lt;MyHomePage&amp;gt; { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.</description>
      <content:encoded><![CDATA[<h3 id="gridview">GridView</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() {
</span></span><span style="display:flex;"><span>  runApp(<span style="color:#66d9ef">const</span> MyApp());
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatelessWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// This widget is the root of your application.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>      title: <span style="color:#e6db74">&#39;Flutter Demo&#39;</span>,
</span></span><span style="display:flex;"><span>      theme: ThemeData(
</span></span><span style="display:flex;"><span>        primarySwatch: Colors.blue,
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>      home: <span style="color:#66d9ef">const</span> MyHomePage(title: <span style="color:#e6db74">&#39;Flutter Demo Home Page&#39;</span>),
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyHomePage</span> <span style="color:#66d9ef">extends</span> StatefulWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> MyHomePage({Key<span style="color:#f92672">?</span> key, <span style="color:#66d9ef">required</span> <span style="color:#66d9ef">this</span>.title}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">String</span> title;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  State<span style="color:#f92672">&lt;</span>MyHomePage<span style="color:#f92672">&gt;</span> createState() <span style="color:#f92672">=&gt;</span> _MyHomePageState();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_MyHomePageState</span> <span style="color:#66d9ef">extends</span> State<span style="color:#f92672">&lt;</span>MyHomePage<span style="color:#f92672">&gt;</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> Scaffold(
</span></span><span style="display:flex;"><span>      appBar: AppBar(
</span></span><span style="display:flex;"><span>        title: Text(widget.title),
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>      body: GridView.builder(
</span></span><span style="display:flex;"><span>        gridDelegate: <span style="color:#66d9ef">const</span> SliverGridDelegateWithFixedCrossAxisCount(
</span></span><span style="display:flex;"><span>          crossAxisCount: <span style="color:#ae81ff">4</span>,    <span style="color:#75715e">//一行显示4个
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          childAspectRatio: <span style="color:#ae81ff">16</span> <span style="color:#f92672">/</span> <span style="color:#ae81ff">9</span>  <span style="color:#75715e">//方块比例
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        ),
</span></span><span style="display:flex;"><span>       itemBuilder: (_, index)<span style="color:#f92672">=&gt;</span>Container(
</span></span><span style="display:flex;"><span>         color: Colors.primaries[index<span style="color:#f92672">%</span>Colors.primaries.length],
</span></span><span style="display:flex;"><span>       )),
</span></span><span style="display:flex;"><span>      floatingActionButton: FloatingActionButton(
</span></span><span style="display:flex;"><span>        onPressed: () {},
</span></span><span style="display:flex;"><span>        tooltip: <span style="color:#e6db74">&#39;Increment&#39;</span>,
</span></span><span style="display:flex;"><span>        child: <span style="color:#66d9ef">const</span> Icon(Icons.add),
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/Lh4lFTVkOwHa7mz.png" alt="截屏2021-11-02 下午10.34.16" style="zoom:25%;" />
<p>​		使用<code>SliverGridDelegateWithFixedCrossAxisCount</code>时，屏幕一行显示的数量已经确定，但是当手机横过来时，就会出现不那么协调的情况，产生了格子的拉伸，而且没有起到显示更多内容的效果：</p>
<img src="https://i.loli.net/2021/11/03/kNI2efp6Bub4KzU.png" alt="截屏2021-11-02 下午10.36.27" style="zoom: 25%;" />
<p>此时我们可以使用<code>SliverGridDelegateWithMaxCrossAxisExtent</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>gridDelegate: <span style="color:#66d9ef">const</span> SliverGridDelegateWithMaxCrossAxisExtent(
</span></span><span style="display:flex;"><span>  maxCrossAxisExtent: <span style="color:#ae81ff">100</span>,    <span style="color:#75715e">//一个方块最多显示100像素
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  childAspectRatio: <span style="color:#ae81ff">16</span> <span style="color:#f92672">/</span> <span style="color:#ae81ff">9</span>,  <span style="color:#75715e">//方块比例
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  mainAxisSpacing: <span style="color:#ae81ff">4.0</span>,     <span style="color:#75715e">//主轴间距
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  crossAxisSpacing: <span style="color:#ae81ff">2.0</span>     <span style="color:#75715e">//副轴间距
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//这里也可以写成GridView.extent，直接指定maxCrossAxisExtent属性，可惜的是这样就不支持动态懒加载（builder）了
</span></span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/szP7UiAMVwN6CLd.png" alt="截屏2021-11-02 下午10.45.10" style="zoom:25%;" />
]]></content:encoded>
    </item>
    
    <item>
      <title>Flutter Sliver组件全解</title>
      <link>https://widegram.com/2021/11/flutter-sliver/</link>
      <pubDate>Wed, 03 Nov 2021 01:47:00 +0000</pubDate>
      
      <guid>https://widegram.com/2021/11/flutter-sliver/</guid>
      <description>一、初试Sliver 1.1 使用Sliver制作一个ListView ​	ListView的底层原理就是Sliver，我们可以用CustomScrollView先做出一个视窗（所谓视窗就是里面的内容比边框大），在其中使用SliverChildBuilderDelegate，而事实上，这也正是ListViewBuilder的底层做法。
CustomScrollView(	//定义一个视窗 slivers: [ SliverList(delegate: SliverChildBuilderDelegate((context, index) { return Container( height: 200, color: Colors.primaries[index%Colors.primaries.length], ); })) ], ) 1.2 更加灵活的ListView——加入任意Widget ​	而使用Sliver，我们就可以实现更加灵活的功能，例如在顶部加入一个FlutterLogo：
CustomScrollView( slivers: [ SliverToBoxAdapter(child: FlutterLogo(size:100),),	//加入一个FlutterLogo SliverList( delegate: SliverChildBuilderDelegate((context, index) { //使用SliverChildBuilderDelegate实现动态加载（Builder） //delegate: SliverChildListDelegate对应不用Builder的ListView return Container( height: 200, color: Colors.primaries[index%Colors.primaries.length], ); })) ], ) 1.3 更加灵活的ListView——组合GridView ​	我们也可以尝试组合GridView：
CustomScrollView( slivers: [ /* 1. 用SliverToBoxAdapter包裹的普通Widget */ const SliverToBoxAdapter(child: FlutterLogo(size:100),), /* 2. 用SliverGrid表示的GridView */ SliverGrid( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3), delegate: SliverChildBuilderDelegate((context, index) =&amp;gt; Container( color: Colors.</description>
      <content:encoded><![CDATA[<h3 id="一初试sliver">一、初试Sliver</h3>
<h4 id="11-使用sliver制作一个listview">1.1 使用Sliver制作一个ListView</h4>
<p>​		ListView的底层原理就是Sliver，我们可以用<code>CustomScrollView</code>先做出一个视窗（所谓视窗就是里面的内容比边框大），在其中使用<code>SliverChildBuilderDelegate</code>，而事实上，这也正是ListViewBuilder的底层做法。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>CustomScrollView(		<span style="color:#75715e">//定义一个视窗
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  slivers: [
</span></span><span style="display:flex;"><span>    SliverList(delegate: SliverChildBuilderDelegate((context, index) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">return</span> Container(
</span></span><span style="display:flex;"><span>        height: <span style="color:#ae81ff">200</span>, 
</span></span><span style="display:flex;"><span>        color: Colors.primaries[index<span style="color:#f92672">%</span>Colors.primaries.length],
</span></span><span style="display:flex;"><span>      );
</span></span><span style="display:flex;"><span>    }))
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/32aMcJSl1Bu9zYE.png" alt="截屏2021-11-02 下午10.19.31" style="zoom: 25%;" />
<h4 id="12-更加灵活的listview加入任意widget">1.2 更加灵活的ListView——加入任意Widget</h4>
<p>​		而使用Sliver，我们就可以实现更加灵活的功能，例如在顶部加入一个FlutterLogo：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>CustomScrollView(
</span></span><span style="display:flex;"><span>  slivers: [
</span></span><span style="display:flex;"><span>    SliverToBoxAdapter(child: FlutterLogo(size:<span style="color:#ae81ff">100</span>),),	<span style="color:#75715e">//加入一个FlutterLogo
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    SliverList(
</span></span><span style="display:flex;"><span>      delegate: SliverChildBuilderDelegate((context, index) {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//使用SliverChildBuilderDelegate实现动态加载（Builder）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        <span style="color:#75715e">//delegate: SliverChildListDelegate对应不用Builder的ListView
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        <span style="color:#66d9ef">return</span> Container(
</span></span><span style="display:flex;"><span>          height: <span style="color:#ae81ff">200</span>, 
</span></span><span style="display:flex;"><span>          color: Colors.primaries[index<span style="color:#f92672">%</span>Colors.primaries.length],
</span></span><span style="display:flex;"><span>        );
</span></span><span style="display:flex;"><span>    	}))
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/g7hMdl8nPDjAi3W.png" alt="截屏2021-11-02 下午10.22.03" style="zoom:25%;" />
<h4 id="13-更加灵活的listview组合gridview">1.3 更加灵活的ListView——组合GridView</h4>
<p>​		我们也可以尝试组合GridView：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>CustomScrollView(
</span></span><span style="display:flex;"><span>  slivers: [
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">/* 1. 用SliverToBoxAdapter包裹的普通Widget */</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> SliverToBoxAdapter(child: FlutterLogo(size:<span style="color:#ae81ff">100</span>),),
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">/* 2. 用SliverGrid表示的GridView */</span>
</span></span><span style="display:flex;"><span>    SliverGrid(
</span></span><span style="display:flex;"><span>      gridDelegate: <span style="color:#66d9ef">const</span> SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: <span style="color:#ae81ff">3</span>),
</span></span><span style="display:flex;"><span>      delegate: SliverChildBuilderDelegate((context, index) <span style="color:#f92672">=&gt;</span> Container(
</span></span><span style="display:flex;"><span>        color: Colors.primaries[index <span style="color:#f92672">%</span> Colors.primaries.length]
</span></span><span style="display:flex;"><span>      ), childCount: <span style="color:#ae81ff">6</span>,  <span style="color:#75715e">//也就是GridView中的itemCount
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                                          )
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">/* 3. 用SliverList表示的ListViewer.builder */</span>
</span></span><span style="display:flex;"><span>    SliverList(
</span></span><span style="display:flex;"><span>      delegate: SliverChildBuilderDelegate((context, index) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> Container(
</span></span><span style="display:flex;"><span>          height: <span style="color:#ae81ff">200</span>, 
</span></span><span style="display:flex;"><span>          color: Colors.primaries[index<span style="color:#f92672">%</span>Colors.primaries.length],
</span></span><span style="display:flex;"><span>        );
</span></span><span style="display:flex;"><span>      })),
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/seKE9IT8mzvip4w.png" alt="截屏2021-11-02 下午11.00.19" style="zoom:25%;" />
<h3 id="二其他的几个常用sliver">二、其他的几个常用Sliver</h3>
<h4 id="21-使用itemextentsliverfixedextentlist">2.1 使用itemExtent——SliverFixedExtentList</h4>
<p>​		您肯定还记得ListView中，为了方面滚动条的定位和跳转，我们会通过ListView中的<code>itemExtent</code>属性来固定每一项在主轴上的长度，例如我们需要定位到第100个元素，只需要将其乘上对应的数即可。</p>
<p>​		这里的<code>itemExtent</code>属性不可以用列表内容Widget的<code>widget</code>或<code>height</code>属性替代，因为那样不会获得滚动条的性能优化。</p>
<p>​		在Sliver中我们需要使用<code>SliverFixedExtentList</code>来定义itemExtent属性，这样我们就可以让某些元素有固定的长度，另一些则不必。</p>
<h4 id="22-自适应的itemextentsliverprototypeextentlist">2.2 自适应的itemExtent——SliverPrototypeExtentList</h4>
<p>​		通常我们很难直接得到内容Widget的高度，例如字号可能会因为老年模式而改变，因此我们可以使用<code>SliverPrototypeExtentList</code>来动态确定其<code>itemExtent</code>.</p>
<p>​		<code>SliverPrototypeExtentList</code>需要一个<code>prototypeItem</code>属性，它只是用来测量内容在主轴上的高度，并不会真的渲染到屏幕上。当字号调整时，我们只要配置了<code>prototypeItem</code>属性，它就会自动重新计算其<code>itemExtent</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//自适应的itemExtent
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>SliverPrototypeExtentList(delegate: delegate, prototypeItem: prototypeItem),
</span></span></code></pre></div><h4 id="23-使用pageviewsliverfillviewport">2.3 使用PageView——SliverFillViewport</h4>
<p>​		回想一下<code>PageView</code>组件——其中的每一个项目都会沾满整个屏幕，通过滑动来切换，通常我们可以用它来做软件的开屏教程。</p>
<p>​		在Sliver中，提供了一个叫做<code>SliverFillViewport</code>的组件，它也是PageView的底层原理。</p>
<h4 id="24-自动回缩的华丽导航条sliverappbar">2.4 自动回缩的华丽导航条——SliverAppBar</h4>
<p>​		我们可以使用SliverAppBar制作一个华丽的动态导航条，首先我们要去掉Scaffold中的AppBar属性，并用下面的代码添加到<code>CustomScrollView</code>中：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>SliverAppBar(
</span></span><span style="display:flex;"><span>  floating: <span style="color:#66d9ef">true</span>, <span style="color:#75715e">//稍微一向下拉导航条就出现
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  snap: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//title: Text(&#34;111&#34;),
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  pinned: <span style="color:#66d9ef">false</span>,<span style="color:#75715e">//不固定
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  expandedHeight: <span style="color:#ae81ff">100</span>,<span style="color:#75715e">//导航条高度
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  stretch: <span style="color:#66d9ef">true</span>,<span style="color:#75715e">//使用更长的拉伸（flexibleSpace）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  flexibleSpace: FlexibleSpaceBar( 
</span></span><span style="display:flex;"><span>    background: FlutterLogo(size:<span style="color:#ae81ff">100</span>),<span style="color:#75715e">//导航条中的背景
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    title: Text(<span style="color:#e6db74">&#34;111&#34;</span>),
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//上缩效果
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    collapseMode: CollapseMode.parallax,
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//下拉效果
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    stretchModes: [
</span></span><span style="display:flex;"><span>      StretchMode.blurBackground,
</span></span><span style="display:flex;"><span>      StretchMode.zoomBackground,
</span></span><span style="display:flex;"><span>      StretchMode.fadeTitle
</span></span><span style="display:flex;"><span>    ],
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/mQeOrRHkWsVidCl.png" alt="截屏2021-11-02 下午11.52.39" style="zoom:25%;" />
<h3 id="三sliver转换器">三、Sliver转换器</h3>
<h4 id="31-普通widget的sliver对应版本">3.1 普通Widget的Sliver对应版本</h4>
<p>例如：</p>
<ul>
<li>
<p><code>Opacity</code> =&gt; <code>SliverOpacity</code></p>
</li>
<li>
<p><code>AnimatedOpacity</code> =&gt;<code>SilverAnimatedOpacity</code></p>
</li>
<li>
<p>值得注意的是，<code>SizedBox</code>直接对应空的<code>SliverToBoxAdapter()</code>，也就是不传任何东西</p>
</li>
</ul>
<p>普通Widget基本都能找到其Sliver形式，可以直接用sliver属性嵌套（相当于普通Widget的child）属性。</p>
<h4 id="32-特殊sliver">3.2 特殊Sliver</h4>
<ol>
<li><code>SliverFillRemaining</code>——占据当前视窗的剩余全部空间，但最多占满一整页。</li>
<li>注意：使用<code>SliverLayoutBuilder</code>可以自己作出一个SliverFillRemaining组件哦～</li>
</ol>
<p>完整实例：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() {
</span></span><span style="display:flex;"><span>  runApp(<span style="color:#66d9ef">const</span> MyApp());
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatelessWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>      title: <span style="color:#e6db74">&#39;Flutter Demo&#39;</span>,
</span></span><span style="display:flex;"><span>      theme: ThemeData(
</span></span><span style="display:flex;"><span>        primarySwatch: Colors.blue,
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>      home: <span style="color:#66d9ef">const</span> MyHomePage(title: <span style="color:#e6db74">&#39;Flutter Demo Home Page&#39;</span>),
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyHomePage</span> <span style="color:#66d9ef">extends</span> StatefulWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> MyHomePage({Key<span style="color:#f92672">?</span> key, <span style="color:#66d9ef">required</span> <span style="color:#66d9ef">this</span>.title}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">String</span> title;
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  State<span style="color:#f92672">&lt;</span>MyHomePage<span style="color:#f92672">&gt;</span> createState() <span style="color:#f92672">=&gt;</span> _MyHomePageState();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_MyHomePageState</span> <span style="color:#66d9ef">extends</span> State<span style="color:#f92672">&lt;</span>MyHomePage<span style="color:#f92672">&gt;</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> Scaffold(
</span></span><span style="display:flex;"><span>      body: <span style="color:#66d9ef">const</span> CustomScrollView(
</span></span><span style="display:flex;"><span>        slivers: [
</span></span><span style="display:flex;"><span>      SliverAppBar(
</span></span><span style="display:flex;"><span>        floating: <span style="color:#66d9ef">true</span>, <span style="color:#75715e">//稍微一向下拉导航条就出现
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        snap: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//title: Text(&#34;111&#34;),
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        pinned: <span style="color:#66d9ef">false</span>,<span style="color:#75715e">//不固定
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        expandedHeight: <span style="color:#ae81ff">100</span>,<span style="color:#75715e">//导航条高度
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        stretch: <span style="color:#66d9ef">true</span>,<span style="color:#75715e">//使用更长的拉伸（flexibleSpace）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        flexibleSpace: FlexibleSpaceBar( 
</span></span><span style="display:flex;"><span>          background: FlutterLogo(size:<span style="color:#ae81ff">100</span>),<span style="color:#75715e">//导航条中的背景
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          title: Text(<span style="color:#e6db74">&#34;111&#34;</span>),
</span></span><span style="display:flex;"><span>          <span style="color:#75715e">//上缩效果
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          collapseMode: CollapseMode.parallax,
</span></span><span style="display:flex;"><span>          <span style="color:#75715e">//下拉效果
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          stretchModes: [
</span></span><span style="display:flex;"><span>            StretchMode.blurBackground,
</span></span><span style="display:flex;"><span>            StretchMode.zoomBackground,
</span></span><span style="display:flex;"><span>            StretchMode.fadeTitle
</span></span><span style="display:flex;"><span>          ],
</span></span><span style="display:flex;"><span>        ),
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>      SliverToBoxAdapter(child: FlutterLogo(size: <span style="color:#ae81ff">100</span>)),
</span></span><span style="display:flex;"><span>      SliverToBoxAdapter(child: FlutterLogo(size: <span style="color:#ae81ff">100</span>)),
</span></span><span style="display:flex;"><span>      SliverToBoxAdapter(child: FlutterLogo(size: <span style="color:#ae81ff">100</span>)),
</span></span><span style="display:flex;"><span>      SliverFillRemaining(
</span></span><span style="display:flex;"><span>        child:Center(	<span style="color:#75715e">//用加载圈填满剩余视口
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          child: CircularProgressIndicator(),
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>      )
</span></span><span style="display:flex;"><span>        ],
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/WuPeF23tlAsoYRy.png" alt="截屏2021-11-03 上午12.44.52" style="zoom:25%;" />
<h4 id="33-sliverappbar原理sliverpersistentheader">3.3 SliverAppBar原理——SliverPersistentHeader</h4>
<p>​	使用原理类似，</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>SliverToBoxAdapter(),
</span></span><span style="display:flex;"><span><span style="color:#75715e">//防止SliverPersistentHeader成为最顶层的Sliver，以至于无法上拉刷新
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>SliverPersistentHeader(
</span></span><span style="display:flex;"><span>	delegate:
</span></span><span style="display:flex;"><span>  pinned: <span style="color:#66d9ef">true</span>,<span style="color:#75715e">//像AppBar一样钉在最顶部
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>)
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>Flutter动画全解（三）底层自定义动画——Hero、CustomPainter和Rive</title>
      <link>https://widegram.com/2021/11/Flutter%E5%8A%A8%E7%94%BB%E5%85%A8%E8%A7%A3%EF%BC%88%E4%B8%89%EF%BC%89-%E5%BA%95%E5%B1%82%E8%87%AA%E5%AE%9A%E4%B9%89%E5%8A%A8%E7%94%BB/</link>
      <pubDate>Mon, 01 Nov 2021 20:40:45 +0000</pubDate>
      
      <guid>https://widegram.com/2021/11/Flutter%E5%8A%A8%E7%94%BB%E5%85%A8%E8%A7%A3%EF%BC%88%E4%B8%89%EF%BC%89-%E5%BA%95%E5%B1%82%E8%87%AA%E5%AE%9A%E4%B9%89%E5%8A%A8%E7%94%BB/</guid>
      <description>底层自定义动画 3.1 Flutter动画原理 ​	Flutter内部通过setState()函数刷新页面，且flutter优化得相当好，一秒执行60/120次也不会有卡顿。
​	Ticker原理：屏幕每刷新一帧，称为一个ticker：
Ticker _ticker = Ticker((elapsed) { print(&amp;#34;Tick: $elapsed&amp;#34;);	//elapsed是运行总时长 })..start(); ​	你可以通过下列方式手动制作一个Ticker（仅作为实验用途，不推荐）：
double _height = 300; @override void initState(){ Ticker _ticker = Ticker((_) =&amp;gt; setState((){ _height --; if(_height &amp;lt;= 0) _height = 300; }))..start(); super.initState(); } ​	上述程序虽然可以运行，但首先，它没有考虑一些特殊情况，如程序暂时退出、切换到其他程序时，动画应该暂停。其次，每一帧时_height--，则在60帧的屏幕下该动画需要5秒执行完毕，而在120hz屏幕下只需要2.5秒。
3.2 Hero动画（主动画） ​	将需要做Hero动画的控件包裹上Hero，并给它一个tag属性，两个页面对应控件的tag一致。
​	如果两个页面中的控件样式不一致，或者类型不统一，那么会瞬间切换，不会有控件切换时的动画效果。
3.3 CustomPaint ​	Tips：要使Container占满屏幕，使用
Container( constraints: BoxConstraints.expand(), //或者 width: double.infinity, height: double.infinity, ) 3.4 嵌入式Rive（Flare）插件动画 Rive New File</description>
      <content:encoded><![CDATA[<h2 id="底层自定义动画">底层自定义动画</h2>
<h3 id="31-flutter动画原理">3.1 Flutter动画原理</h3>
<p>​		Flutter内部通过<code>setState()</code>函数刷新页面，且flutter优化得相当好，一秒执行60/120次也不会有卡顿。</p>
<p>​		Ticker原理：屏幕每刷新一帧，称为一个ticker：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Ticker _ticker <span style="color:#f92672">=</span> Ticker((elapsed) {
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;Tick: </span><span style="color:#e6db74">$</span>elapsed<span style="color:#e6db74">&#34;</span>);	<span style="color:#75715e">//elapsed是运行总时长
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>})..start();
</span></span></code></pre></div><p>​		你可以通过下列方式手动制作一个Ticker（仅作为实验用途，不推荐）：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">double</span> _height <span style="color:#f92672">=</span> <span style="color:#ae81ff">300</span>;
</span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> initState(){
</span></span><span style="display:flex;"><span>  Ticker _ticker <span style="color:#f92672">=</span> Ticker((_) <span style="color:#f92672">=&gt;</span> setState((){
</span></span><span style="display:flex;"><span>    _height <span style="color:#f92672">--</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span>(_height <span style="color:#f92672">&lt;=</span> <span style="color:#ae81ff">0</span>) _height <span style="color:#f92672">=</span> <span style="color:#ae81ff">300</span>;
</span></span><span style="display:flex;"><span>  }))..start();
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">super</span>.initState();
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>​		上述程序虽然可以运行，但首先，它没有考虑一些特殊情况，如程序暂时退出、切换到其他程序时，动画应该暂停。其次，每一帧时<code>_height--</code>，则在60帧的屏幕下该动画需要5秒执行完毕，而在120hz屏幕下只需要2.5秒。</p>
<h3 id="32-hero动画主动画">3.2 Hero动画（主动画）</h3>
<p>​		将需要做Hero动画的控件包裹上Hero，并给它一个tag属性，两个页面对应控件的tag一致。</p>
<p>​		如果两个页面中的控件样式不一致，或者类型不统一，那么会瞬间切换，不会有控件切换时的动画效果。</p>
<h3 id="33-custompaint">3.3 CustomPaint</h3>
<p>​		Tips：要使Container占满屏幕，使用</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>	constraints: BoxConstraints.expand(),
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//或者
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  width: <span style="color:#66d9ef">double</span>.infinity,
</span></span><span style="display:flex;"><span>  height: <span style="color:#66d9ef">double</span>.infinity,
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><h3 id="34-嵌入式riveflare插件动画">3.4 嵌入式Rive（Flare）插件动画</h3>
<p><a href="https://rive.app/a/im-bradley/files/Flare/new-file" target="_blank">Rive New File</a></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Flutter动画全解（二）显式动画——Transition系列</title>
      <link>https://widegram.com/2021/11/Flutter%E5%8A%A8%E7%94%BB%E5%85%A8%E8%A7%A3%EF%BC%88%E4%BA%8C%EF%BC%89%E6%98%BE%E5%BC%8F%E5%8A%A8%E7%94%BB-Transition%E7%B3%BB%E5%88%97/</link>
      <pubDate>Mon, 01 Nov 2021 20:33:45 +0000</pubDate>
      
      <guid>https://widegram.com/2021/11/Flutter%E5%8A%A8%E7%94%BB%E5%85%A8%E8%A7%A3%EF%BC%88%E4%BA%8C%EF%BC%89%E6%98%BE%E5%BC%8F%E5%8A%A8%E7%94%BB-Transition%E7%B3%BB%E5%88%97/</guid>
      <description>显式动画——Transition系列（配合AnimationController） 2.1 循环旋转——RotationTransition ​	我们需要使用RotationTransition配合AnimationController实现动画的手动控制，以实现隐式动画（Animated系列）无法完成的功能：循环重播、随时中断、多方协调等。
​	另外，要使用AnimationController，我们必须使用StatefulWidget，以使用生命周期函数initState()和dispose()。
​	我们先简单地看一个实例，在后文中，我们将详细解释其中的内容。
import &amp;#39;package:flutter/material.dart&amp;#39;; void main() =&amp;gt; runApp(MyApp()); class MyApp extends StatefulWidget { MyApp({Key? key}) : super(key: key); @override _MyAppState createState() =&amp;gt; _MyAppState(); } //SingleTickerProviderStateMixin用于垂直同步 class _MyAppState extends State&amp;lt;MyApp&amp;gt; with SingleTickerProviderStateMixin { late AnimationController _controller; bool _loading = false; @override void initState() { _controller = AnimationController( duration: const Duration(seconds: 1), vsync: this, ); super.initState(); } @override void dispose() { _controller.dispose(); //用完删除，否则内存泄漏 super.dispose(); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Center( child: Container( width: 300, height: 120, color: Colors.</description>
      <content:encoded><![CDATA[<h2 id="显式动画transition系列配合animationcontroller">显式动画——Transition系列（配合AnimationController）</h2>
<h3 id="21-循环旋转rotationtransition">2.1 循环旋转——RotationTransition</h3>
<p>​		我们需要使用<code>RotationTransition</code>配合<code>AnimationController</code>实现动画的手动控制，以实现隐式动画（Animated系列）无法完成的功能：循环重播、随时中断、多方协调等。</p>
<p>​		另外，要使用AnimationController，我们必须使用StatefulWidget，以使用生命周期函数<code>initState()</code>和<code>dispose()</code>。</p>
<p>​		我们先简单地看一个实例，在后文中，我们将详细解释其中的内容。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatefulWidget {
</span></span><span style="display:flex;"><span>  MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  _MyAppState createState() <span style="color:#f92672">=&gt;</span> _MyAppState();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//SingleTickerProviderStateMixin用于垂直同步
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_MyAppState</span> <span style="color:#66d9ef">extends</span> State<span style="color:#f92672">&lt;</span>MyApp<span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">with</span> SingleTickerProviderStateMixin {
</span></span><span style="display:flex;"><span>  late AnimationController _controller;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">bool</span> _loading <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> initState() {
</span></span><span style="display:flex;"><span>    _controller <span style="color:#f92672">=</span> AnimationController(
</span></span><span style="display:flex;"><span>      duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>      vsync: <span style="color:#66d9ef">this</span>,
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.initState();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> dispose() {
</span></span><span style="display:flex;"><span>    _controller.dispose(); <span style="color:#75715e">//用完删除，否则内存泄漏
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">super</span>.dispose();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>        home: Scaffold(
</span></span><span style="display:flex;"><span>      body: Center(
</span></span><span style="display:flex;"><span>          child: Container(
</span></span><span style="display:flex;"><span>        width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>        height: <span style="color:#ae81ff">120</span>,
</span></span><span style="display:flex;"><span>        color: Colors.blue,
</span></span><span style="display:flex;"><span>        child: RotationTransition(
</span></span><span style="display:flex;"><span>          turns: _controller,   <span style="color:#75715e">//这里直接使用controller即可，后文来解释
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          <span style="color:#75715e">//这里的动画还没有开始，调用_controller.forward()开始
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          child: <span style="color:#66d9ef">const</span> Icon(Icons.repeat,size: <span style="color:#ae81ff">100</span>),
</span></span><span style="display:flex;"><span>        ),
</span></span><span style="display:flex;"><span>      )),
</span></span><span style="display:flex;"><span>      floatingActionButton: FloatingActionButton(
</span></span><span style="display:flex;"><span>        child: <span style="color:#66d9ef">const</span> Icon(Icons.add),
</span></span><span style="display:flex;"><span>        onPressed: () {		<span style="color:#75715e">//按一下按钮开始旋转，再按一下停止。
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          _loading <span style="color:#f92672">?</span> _controller.stop() <span style="color:#f92672">:</span> _controller.repeat();
</span></span><span style="display:flex;"><span>          _loading <span style="color:#f92672">=</span> <span style="color:#f92672">!</span>_loading;
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>完成效果如下：</p>
<img src="https://i.loli.net/2021/11/01/yfwxzL6BWpk1F5E.jpg" alt="IMG_0848" style="zoom: 25%;" />
<h3 id="22-animationcontroller是什么">2.2 AnimationController是什么</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">AnimationController</span> <span style="color:#66d9ef">extends</span> Animation<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">double</span><span style="color:#f92672">&gt;</span>
</span></span></code></pre></div><p>​		从本质上来说，<code>AnimationController</code>是一个<code>Animation&lt;double&gt;</code>，正如上面看到的那样，<code>RotationTransition</code>的turns属性本质上要求一个Animation。</p>
<p>​		AnimationController会在Duration时间内，生成0～1的double，如果要更改区间，使用lowerBound和upperBound属性。</p>
<p>​		示例代码：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatefulWidget {
</span></span><span style="display:flex;"><span>  MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  _MyAppState createState() <span style="color:#f92672">=&gt;</span> _MyAppState();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//SingleTickerProviderStateMixin用于垂直同步，屏幕每次刷新，产生一个tick
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_MyAppState</span> <span style="color:#66d9ef">extends</span> State<span style="color:#f92672">&lt;</span>MyApp<span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">with</span> SingleTickerProviderStateMixin {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">/* class AnimationController extends Animation&lt;double&gt; */</span>
</span></span><span style="display:flex;"><span>  late AnimationController _controller;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">bool</span> _loading <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> initState() {
</span></span><span style="display:flex;"><span>    _controller <span style="color:#f92672">=</span> AnimationController(
</span></span><span style="display:flex;"><span>      duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>      lowerBound: <span style="color:#ae81ff">0.0</span>, <span style="color:#75715e">//与默认一致，生成0-1
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      upperBound: <span style="color:#ae81ff">1.0</span>,
</span></span><span style="display:flex;"><span>      vsync: <span style="color:#66d9ef">this</span>,
</span></span><span style="display:flex;"><span>    )..addListener(() { <span style="color:#75715e">//dart语法：“..”表示返回值还是前面的表达式
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      <span style="color:#75715e">// _controller.value 在1s内自动生成0～1之间的数字（1秒60帧）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      print(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>_controller.value<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.initState();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> dispose() {
</span></span><span style="display:flex;"><span>    _controller.dispose(); <span style="color:#75715e">//用完删除，否则内存泄漏
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">super</span>.dispose();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>        home: Scaffold(
</span></span><span style="display:flex;"><span>      body: Center(
</span></span><span style="display:flex;"><span>          child: Container(
</span></span><span style="display:flex;"><span>        width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>        height: <span style="color:#ae81ff">120</span>,
</span></span><span style="display:flex;"><span>        color: Colors.blue,
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//例如：RotateTransition、FadeTransition、ScaleTransition
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        child: FadeTransition(
</span></span><span style="display:flex;"><span>          opacity: _controller, <span style="color:#75715e">//这里本质上要求一个Animation&lt;double&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          child: <span style="color:#66d9ef">const</span> Icon(Icons.repeat, size: <span style="color:#ae81ff">100</span>),
</span></span><span style="display:flex;"><span>        ),
</span></span><span style="display:flex;"><span>      )),
</span></span><span style="display:flex;"><span>      floatingActionButton: FloatingActionButton(
</span></span><span style="display:flex;"><span>        child: <span style="color:#66d9ef">const</span> Icon(Icons.add),
</span></span><span style="display:flex;"><span>        onPressed: () {
</span></span><span style="display:flex;"><span>          _loading <span style="color:#f92672">?</span> _controller.stop() <span style="color:#f92672">:</span> _controller.repeat(reverse: <span style="color:#66d9ef">true</span>);
</span></span><span style="display:flex;"><span>          _loading <span style="color:#f92672">=</span> <span style="color:#f92672">!</span>_loading;
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="23-控制器补间tween和曲线">2.3 控制器补间（Tween）和曲线</h3>
<p>​		我们可以使用drive函数将Animation的值映射到Tween上，也就是说，最终生成的值是在0.5到1.0之间，而不是原来的0.0～1.0了。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>ScaleTransition(
</span></span><span style="display:flex;"><span>  scale: _controller.drive(Tween(begin: <span style="color:#ae81ff">0.5</span>, end: <span style="color:#ae81ff">1.0</span>)),
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><p>​		又比如说，</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>SlideTransition(
</span></span><span style="display:flex;"><span>  position: _controller.drive(Tween(begin: Offset.zero, end: <span style="color:#66d9ef">const</span> Offset(<span style="color:#ae81ff">0.1</span>,<span style="color:#ae81ff">0</span>))),
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>),
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//等价于
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>SlideTransition(
</span></span><span style="display:flex;"><span>  position: Tween(begin: Offset.zero, end: <span style="color:#66d9ef">const</span> Offset(<span style="color:#ae81ff">0.1</span>,<span style="color:#ae81ff">0</span>)).animate(_controller),
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><p>​		通过使用chain函数，Tween之间可以叠加，比如，</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>SlideTransition(
</span></span><span style="display:flex;"><span>  position: Tween(begin: Offset.zero, end: <span style="color:#66d9ef">const</span> Offset(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">1.0</span>))
</span></span><span style="display:flex;"><span>      .chain(CurveTween(curve: Curves.elasticOut))  <span style="color:#75715e">//叠加曲线
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      .chain(CurveTween(curve: <span style="color:#66d9ef">const</span> Interval(<span style="color:#ae81ff">0.5</span>, <span style="color:#ae81ff">1.0</span>))) 
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">//使用Interval，限制动画播放时间，从 50% 时间开始到 100% ，其他时间不做任何事
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      .animate(_controller),
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><p>​		整体代码如下：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatefulWidget {
</span></span><span style="display:flex;"><span>  MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  _MyAppState createState() <span style="color:#f92672">=&gt;</span> _MyAppState();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_MyAppState</span> <span style="color:#66d9ef">extends</span> State<span style="color:#f92672">&lt;</span>MyApp<span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">with</span> SingleTickerProviderStateMixin {
</span></span><span style="display:flex;"><span>  late AnimationController _controller;
</span></span><span style="display:flex;"><span>  late <span style="color:#66d9ef">bool</span> _stop <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> initState() {
</span></span><span style="display:flex;"><span>    _controller <span style="color:#f92672">=</span> AnimationController(
</span></span><span style="display:flex;"><span>      duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>      vsync: <span style="color:#66d9ef">this</span>,
</span></span><span style="display:flex;"><span>    )..repeat();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.initState();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> dispose() {
</span></span><span style="display:flex;"><span>    _controller.dispose();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.dispose();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>        home: Scaffold(
</span></span><span style="display:flex;"><span>      body: Center(
</span></span><span style="display:flex;"><span>          child: SlideTransition(
</span></span><span style="display:flex;"><span>              position: Tween(begin: Offset.zero, end: <span style="color:#66d9ef">const</span> Offset(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">1.0</span>))
</span></span><span style="display:flex;"><span>                .chain(CurveTween(curve: Curves.elasticOut))  <span style="color:#75715e">//叠加曲线
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                .chain(CurveTween(curve: <span style="color:#66d9ef">const</span> Interval(<span style="color:#ae81ff">0.5</span>, <span style="color:#ae81ff">1.0</span>))) 
</span></span><span style="display:flex;"><span>                      <span style="color:#75715e">//使用Interval，限制动画播放时间，从 50% 时间开始到 100% ，其他时间不做任何事
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                .animate(_controller),
</span></span><span style="display:flex;"><span>              child: Container(
</span></span><span style="display:flex;"><span>                width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>                height: <span style="color:#ae81ff">120</span>,
</span></span><span style="display:flex;"><span>                color: Colors.blue,
</span></span><span style="display:flex;"><span>              ))),
</span></span><span style="display:flex;"><span>      floatingActionButton: FloatingActionButton(
</span></span><span style="display:flex;"><span>        child: <span style="color:#66d9ef">const</span> Icon(Icons.add),
</span></span><span style="display:flex;"><span>        onPressed: () {
</span></span><span style="display:flex;"><span>          _stop <span style="color:#f92672">?</span> _controller.repeat() <span style="color:#f92672">:</span> _controller.reset();
</span></span><span style="display:flex;"><span>          _stop <span style="color:#f92672">=</span> <span style="color:#f92672">!</span>_stop;
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="24-交错动画interval">2.4 交错动画——Interval</h3>
<p>​		如上文，使用Interval可以限制播放时间，如此一来，作如下安排，达到五个组件间到交错动画：</p>
<ol>
<li>
<p>时间0.0～0.2运行第一个动画</p>
</li>
<li>
<p>时间0.2～0.4运行第二个动画</p>
</li>
<li>
<p>时间0.4～0.6运行第三个动画</p>
</li>
<li>
<p>时间0.6～0.8运行第四个动画</p>
</li>
<li>
<p>时间0.8～1.0运行第五个动画</p>
<p>​	使用<code>Column</code>控件将以上五个<code>SlideTransition</code>组织起来，并将它们绑定到同一个_controller即可（SlideTransition本身也是Widget）：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">SlideTransition</span> <span style="color:#66d9ef">extends</span> AnimatedWidget { ... }
</span></span></code></pre></div></li>
</ol>
<h3 id="25-自定义动画animatedbuilder">2.5 自定义动画——AnimatedBuilder</h3>
<p>​		通过自己动手制作FadeTransition，我们可以看出Transition系列控件的内部原理。同时也可以看到，就像TweenAnimationBuilder那样，使用AnimatedBuilder时我们也可以对动画进行优化，避免反复渲染那些与动画无关的Widget：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//如何使用AnimatedBuilder制作出一个FadeTransition？
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>child: AnimatedBuilder(
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//每当animtion（_controller）的值变化，flutter就会重新调用builder函数
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  animation: _controller, 
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//还记得builder就是复杂版的child吗？
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  builder: (context, child) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> Opacity(
</span></span><span style="display:flex;"><span>      opacity: _controller.value,<span style="color:#75715e">//这里可以用Tween制作补间动画
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      child: child
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//下面的child是用于优化
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  child: <span style="color:#66d9ef">const</span> Icon(Icons.repeat, size: <span style="color:#ae81ff">100</span>)
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//这是内置的FadeTransition，使用起来更加简单，但是没有办法进行细致的绘制优化
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>child: FadeTransition(
</span></span><span style="display:flex;"><span>  opacity: _controller, <span style="color:#75715e">//这里本质上要求一个Animation&lt;double&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  child: <span style="color:#66d9ef">const</span> Icon(Icons.repeat, size: <span style="color:#ae81ff">100</span>),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><p>​		整体代码如下：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatefulWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  _MyAppState createState() <span style="color:#f92672">=&gt;</span> _MyAppState();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_MyAppState</span> <span style="color:#66d9ef">extends</span> State<span style="color:#f92672">&lt;</span>MyApp<span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">with</span> SingleTickerProviderStateMixin {
</span></span><span style="display:flex;"><span>  late AnimationController _controller;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> initState() {
</span></span><span style="display:flex;"><span>    _controller <span style="color:#f92672">=</span> AnimationController(
</span></span><span style="display:flex;"><span>      duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">2</span>),
</span></span><span style="display:flex;"><span>      vsync: <span style="color:#66d9ef">this</span>,
</span></span><span style="display:flex;"><span>    )..repeat();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.initState();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> dispose() {
</span></span><span style="display:flex;"><span>    _controller.dispose();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.dispose();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">final</span> Animation opacityAnimation <span style="color:#f92672">=</span> Tween(begin: <span style="color:#ae81ff">0.5</span>, end: <span style="color:#ae81ff">0.8</span>).animate(_controller);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>        home: Scaffold(
</span></span><span style="display:flex;"><span>      body: Center(
</span></span><span style="display:flex;"><span>          child: Container(
</span></span><span style="display:flex;"><span>              width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>              height: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>              color: Colors.red,
</span></span><span style="display:flex;"><span>              child: AnimatedBuilder(
</span></span><span style="display:flex;"><span>                  animation: _controller, <span style="color:#75715e">//每当animtion（_controller）的值变化，flutter就会重新调用builder函数
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                  builder: (context, child) { <span style="color:#75715e">//还记得builder就是复杂版的child吗？
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                    <span style="color:#66d9ef">return</span> Opacity(
</span></span><span style="display:flex;"><span>                      opacity: opacityAnimation.value,
</span></span><span style="display:flex;"><span>                      <span style="color:#75715e">//使用Tween制作补间
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                      <span style="color:#75715e">//需要注意的是这里的opacity是普通变量，所以需要使用Animation.value
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                      child: child
</span></span><span style="display:flex;"><span>                    );
</span></span><span style="display:flex;"><span>                  },
</span></span><span style="display:flex;"><span>                  <span style="color:#75715e">//下面的child是用于优化
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                  child: <span style="color:#66d9ef">const</span> Icon(Icons.repeat, size: <span style="color:#ae81ff">100</span>)
</span></span><span style="display:flex;"><span>              ))),
</span></span><span style="display:flex;"><span>      floatingActionButton: FloatingActionButton(
</span></span><span style="display:flex;"><span>        child: <span style="color:#66d9ef">const</span> Icon(Icons.add),
</span></span><span style="display:flex;"><span>        onPressed: () {},
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="26-使用多个controller的例子tickerproviderstatemixin">2.6 使用多个Controller的例子——TickerProviderStateMixin</h3>
<p>​	有时我们会希望使用多个Controller（绝大多数情况下一个足矣），此时我们需要使用<code>TickerProviderStateMixin</code>。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatefulWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  _MyAppState createState() <span style="color:#f92672">=&gt;</span> _MyAppState();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_MyAppState</span> <span style="color:#66d9ef">extends</span> State<span style="color:#f92672">&lt;</span>MyApp<span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">with</span> TickerProviderStateMixin {
</span></span><span style="display:flex;"><span>  late AnimationController _expansionController;
</span></span><span style="display:flex;"><span>  late AnimationController _opacityController;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> initState() {
</span></span><span style="display:flex;"><span>    _expansionController <span style="color:#f92672">=</span> AnimationController(vsync: <span style="color:#66d9ef">this</span>);
</span></span><span style="display:flex;"><span>    _opacityController <span style="color:#f92672">=</span> AnimationController(vsync: <span style="color:#66d9ef">this</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.initState();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> dispose() {
</span></span><span style="display:flex;"><span>    _expansionController.dispose();
</span></span><span style="display:flex;"><span>    _opacityController.dispose();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.dispose();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">final</span> Animation gradientAnimation <span style="color:#f92672">=</span>
</span></span><span style="display:flex;"><span>        Tween(begin: <span style="color:#ae81ff">0.3</span>, end: <span style="color:#ae81ff">1.0</span>).animate(_expansionController);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>        home: Scaffold(
</span></span><span style="display:flex;"><span>      body: Center(
</span></span><span style="display:flex;"><span>          child: FadeTransition(
</span></span><span style="display:flex;"><span>              opacity: _opacityController.drive(Tween(begin: <span style="color:#ae81ff">1.0</span>, end: <span style="color:#ae81ff">0.5</span>)),
</span></span><span style="display:flex;"><span>              child: AnimatedBuilder(
</span></span><span style="display:flex;"><span>                animation: _expansionController,
</span></span><span style="display:flex;"><span>                builder: (context, child) <span style="color:#f92672">=&gt;</span> Container(
</span></span><span style="display:flex;"><span>                  width: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>                  decoration: BoxDecoration(
</span></span><span style="display:flex;"><span>                    shape: BoxShape.circle,
</span></span><span style="display:flex;"><span>                    color: Colors.white,
</span></span><span style="display:flex;"><span>                    gradient: RadialGradient(colors: [
</span></span><span style="display:flex;"><span>                      Colors.blue.shade500,
</span></span><span style="display:flex;"><span>                      Colors.grey.shade50
</span></span><span style="display:flex;"><span>                    ], stops: [
</span></span><span style="display:flex;"><span>                      gradientAnimation.value,
</span></span><span style="display:flex;"><span>                      gradientAnimation.value <span style="color:#f92672">+</span> <span style="color:#ae81ff">0.2</span>
</span></span><span style="display:flex;"><span>                    ]),
</span></span><span style="display:flex;"><span>                  ),
</span></span><span style="display:flex;"><span>                ),
</span></span><span style="display:flex;"><span>              ))),
</span></span><span style="display:flex;"><span>      floatingActionButton: FloatingActionButton(
</span></span><span style="display:flex;"><span>        child: <span style="color:#66d9ef">const</span> Icon(Icons.badge),
</span></span><span style="display:flex;"><span>        onPressed: () <span style="color:#66d9ef">async</span> {
</span></span><span style="display:flex;"><span>          _expansionController.duration <span style="color:#f92672">=</span> <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">3</span>);
</span></span><span style="display:flex;"><span>          _expansionController.forward();
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">await</span> Future.delayed(<span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">3</span>));
</span></span><span style="display:flex;"><span>          _opacityController.duration <span style="color:#f92672">=</span> <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">4</span>);
</span></span><span style="display:flex;"><span>          _opacityController.forward();
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">await</span> Future.delayed(<span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">4</span>));
</span></span><span style="display:flex;"><span>          _expansionController.duration <span style="color:#f92672">=</span> <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>);
</span></span><span style="display:flex;"><span>          _expansionController.reverse();
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>Flutter动画全解（一）隐式动画——Animated系列</title>
      <link>https://widegram.com/2021/11/Flutter%E5%8A%A8%E7%94%BB%E5%85%A8%E8%A7%A3%EF%BC%88%E4%B8%80%EF%BC%89%E9%9A%90%E5%BC%8F%E5%8A%A8%E7%94%BB-Animated%E7%B3%BB%E5%88%97/</link>
      <pubDate>Mon, 01 Nov 2021 20:19:45 +0000</pubDate>
      
      <guid>https://widegram.com/2021/11/Flutter%E5%8A%A8%E7%94%BB%E5%85%A8%E8%A7%A3%EF%BC%88%E4%B8%80%EF%BC%89%E9%9A%90%E5%BC%8F%E5%8A%A8%E7%94%BB-Animated%E7%B3%BB%E5%88%97/</guid>
      <description>隐式动画——Animated系列 1.1 快速开始——AnimatedContainer //原始main.dart import &amp;#39;package:flutter/material.dart&amp;#39;; void main() =&amp;gt; runApp(MyApp()); class MyApp extends StatelessWidget { MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Container(	//一个普普通通的Container width: 300, height: 300, color: Colors.blue, child: const Center( child: Text(&amp;#34;Hi&amp;#34;, style: TextStyle( fontSize: 72, )), ), ), )); } } //使用AnimatedContainer，并传入Duration，两行代码就可以动起来！ import &amp;#39;package:flutter/material.dart&amp;#39;; void main() =&amp;gt; runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Center( child: AnimatedContainer(	//将Container换成AnimatedContainer duration: const Duration(seconds: 1),	//同时加上duration参数即可 width: 300, height: 100, color: Colors.</description>
      <content:encoded><![CDATA[<p><img loading="lazy" src="https://i.loli.net/2021/11/01/VBE9gcASi5q6jaY.jpg" alt="DBAED6ED-4E11-4344-8500-8F9EA1EA816F_1_105_c.jpeg"  />
</p>
<h2 id="隐式动画animated系列">隐式动画——Animated系列</h2>
<h3 id="11-快速开始animatedcontainer">1.1 快速开始——AnimatedContainer</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//原始main.dart
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatelessWidget {
</span></span><span style="display:flex;"><span>  MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>        home: Scaffold(
</span></span><span style="display:flex;"><span>      body: Container(		<span style="color:#75715e">//一个普普通通的Container
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>        height: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>        color: Colors.blue,
</span></span><span style="display:flex;"><span>        child: <span style="color:#66d9ef">const</span> Center(
</span></span><span style="display:flex;"><span>          child: Text(<span style="color:#e6db74">&#34;Hi&#34;</span>,
</span></span><span style="display:flex;"><span>              style: TextStyle(
</span></span><span style="display:flex;"><span>                fontSize: <span style="color:#ae81ff">72</span>,
</span></span><span style="display:flex;"><span>              )),
</span></span><span style="display:flex;"><span>        ),
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//使用AnimatedContainer，并传入Duration，两行代码就可以动起来！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(<span style="color:#66d9ef">const</span> MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatelessWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>      home: Scaffold(
</span></span><span style="display:flex;"><span>        body: Center(
</span></span><span style="display:flex;"><span>          child: AnimatedContainer(		<span style="color:#75715e">//将Container换成AnimatedContainer
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>            duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),	<span style="color:#75715e">//同时加上duration参数即可
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>            width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>            height: <span style="color:#ae81ff">100</span>,
</span></span><span style="display:flex;"><span>            color: Colors.blue,
</span></span><span style="display:flex;"><span>            child: <span style="color:#66d9ef">const</span> Center(
</span></span><span style="display:flex;"><span>              child: Text(<span style="color:#e6db74">&#34;Hi&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">72</span>)),
</span></span><span style="display:flex;"><span>            ),
</span></span><span style="display:flex;"><span>          ),
</span></span><span style="display:flex;"><span>        ),
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>​		这里，<code>AnimatedContainer</code>不能管理其child属性的变化，只能在其自身属性变化时产生动画。</p>
<p>​		值得一提的是，<code>decoration</code>属性是非常好用的，例如：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>AnimatedContainer(
</span></span><span style="display:flex;"><span>  duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>  width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>  height: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//color: Colors.blue,			//设置decoration属性后，color属性必须删除
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  decoration: BoxDecoration(
</span></span><span style="display:flex;"><span>    gradient: <span style="color:#66d9ef">const</span> LinearGradient(			<span style="color:#75715e">//设置渐变色
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      begin: Alignment.bottomCenter,    <span style="color:#75715e">//橙色从底部中心开始
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      end: Alignment.topCenter,         <span style="color:#75715e">//白色从顶部中心开始
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      colors: [Colors.orange, Colors.white],
</span></span><span style="display:flex;"><span>      stops: [<span style="color:#ae81ff">0.5</span>,<span style="color:#ae81ff">0.7</span>],                 <span style="color:#75715e">//只在这个区间内设置动画效果
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    ),
</span></span><span style="display:flex;"><span>    boxShadow: <span style="color:#66d9ef">const</span> [BoxShadow(spreadRadius: <span style="color:#ae81ff">5</span>, blurRadius: <span style="color:#ae81ff">25</span>)],	<span style="color:#75715e">//边框半径5，阴影半径25
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    borderRadius: BorderRadius.circular(<span style="color:#ae81ff">150</span>)  <span style="color:#75715e">//设置300/2=150，出现正圆形；设置0，变为方形
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  ),
</span></span><span style="display:flex;"><span>  child: <span style="color:#66d9ef">const</span> Center(
</span></span><span style="display:flex;"><span>    child: Text(<span style="color:#e6db74">&#34;Hi&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">72</span>)),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/01/nw7Zi82tVsqjIyr.jpg" alt="5514F21B-7194-4DA1-A5D0-165C90F6B1DC_1_105_c" style="zoom:33%;" />
<p>最终效果图如上。</p>
<h3 id="12-在不同控件间切换animatedswitcher">1.2 在不同控件间切换——AnimatedSwitcher</h3>
<p>​		AnimatedSwitcher主要监控child属性，如果其child属性发生变化，则会在替换控件时添加过渡动画。如果child变为null，那么该控件将渐渐消失。</p>
<p>​		AnimatedSwitcher根据 1.child的属性 2.key属性 来判断控件是否变化。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>AnimatedSwitcher(
</span></span><span style="display:flex;"><span>  transitionBuilder: (child, animation) {   <span style="color:#75715e">//自定义动画
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//return FadeTransition(opacity: animation, child: child);  //默认效果：透明度渐变
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//return ScaleTransition(scale: animation, child: child); //缩放动画
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">return</span> RotationTransition(turns: animation, child: child); <span style="color:#75715e">//旋转动画
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  },
</span></span><span style="display:flex;"><span>  duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),	<span style="color:#75715e">//动画持续1秒
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#75715e">//child: CircularProgressIndicator(),
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  child: Text(<span style="color:#e6db74">&#34;h22i&#34;</span>,
</span></span><span style="display:flex;"><span>  	key: UniqueKey(),     <span style="color:#75715e">//每次都生成一个新的key，因此AnimatedSwitcher每次都会播放切换动画
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  	style: <span style="color:#66d9ef">const</span> TextStyle(fontSize: <span style="color:#ae81ff">100</span>)
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>在<code>transitionBuilder</code>中，也可以嵌套使用多种动画：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>transitionBuilder: (child, animation) {   <span style="color:#75715e">//自定义动画
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#66d9ef">return</span> FadeTransition(   <span style="color:#75715e">//不透明度渐变 + 旋转动画 的嵌套
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    opacity: animation,
</span></span><span style="display:flex;"><span>    child: RotationTransition(turns: animation, child: child), <span style="color:#75715e">//旋转动画
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  );
</span></span><span style="display:flex;"><span>},
</span></span></code></pre></div><p>如果你只有两个控件，要实现来回切换的效果，可以使用优化过后的<code>AnimatedCrossFade</code>控件。</p>
<h3 id="13-曲线curvesbounceout">1.3 曲线——Curves.bounceOut</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>AnimatedOpacity(
</span></span><span style="display:flex;"><span>	duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>	curve: Curves.bounceOut,    <span style="color:#75715e">//设置回弹曲线
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>	opacity: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>	child: Container(width: <span style="color:#ae81ff">300</span>, height: <span style="color:#ae81ff">300</span>, color: Colors.blue),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><p>可以多个Animated控件嵌套，例如：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>AnimatedPadding(
</span></span><span style="display:flex;"><span>  duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>  padding: <span style="color:#66d9ef">const</span> EdgeInsets.only(top: <span style="color:#ae81ff">200.0</span>),
</span></span><span style="display:flex;"><span>  curve: Curves.bounceOut, <span style="color:#75715e">//设置回弹曲线，默认Curves.linear
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  child: AnimatedOpacity(   <span style="color:#75715e">//AnimatedPadding中嵌套AnimatedOpacity，但动画会一同开始
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>    opacity: <span style="color:#ae81ff">0.5</span>,
</span></span><span style="display:flex;"><span>    child: Container(width: <span style="color:#ae81ff">300</span>, height: <span style="color:#ae81ff">300</span>, color: Colors.blue),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><h3 id="14-animated还不够自制补间动画tweenanimationbuilder">1.4 Animated还不够？自制补间动画——TweenAnimationBuilder</h3>
<p>使用<code>TweenAnimationBuilder</code>和<code>Tween</code>来创建补间动画。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//使用TweenAnimationBuilder自制一个AnimatedOpacity
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span>TweenAnimationBuilder(
</span></span><span style="display:flex;"><span>  duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//其中，begin的值只在开始时有效，后期如果修改了end，则会从当前状态（而不是begin）前往新的end
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#75715e">//从begin到end期间，builder函数会被反复调用，反复渲染新的（含value）的控件
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  tween: Tween<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">double</span><span style="color:#f92672">&gt;</span>(begin: <span style="color:#ae81ff">0.0</span>, end: <span style="color:#ae81ff">0.5</span>),   
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//builder是复杂形式的child（如ListBuilder）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  builder: (BuildContext context, <span style="color:#66d9ef">double</span> value, Widget<span style="color:#f92672">?</span> child)<span style="color:#f92672">=&gt;</span> Opacity(
</span></span><span style="display:flex;"><span>    opacity: value,	<span style="color:#75715e">//value也就是tween自动生成的那个值（0.0-0.5之间）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child: child  <span style="color:#75715e">//参数中的child不会被反复渲染，而会使用原来的child，达到一个优化的效果
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  ),
</span></span><span style="display:flex;"><span>  child: Container( 
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//这里的child（也就是直属于TweenAnimationBuilder的child）就是builder函数中的child
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//builder不会反复渲染这里的child，达到一个优化的效果
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>    height: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>    color: Colors.red,
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><p>使用<code>Transform</code>来定义变换：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//旋转变换
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>TweenAnimationBuilder(
</span></span><span style="display:flex;"><span>  duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>  tween: Tween<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">double</span><span style="color:#f92672">&gt;</span>(begin: <span style="color:#ae81ff">1.0</span>, end: <span style="color:#ae81ff">6.0</span>),   <span style="color:#75715e">//忽略begin，则此时begin=end，启动时动画不播放
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  builder: (BuildContext context, <span style="color:#66d9ef">double</span> value, Widget<span style="color:#f92672">?</span> child) <span style="color:#f92672">=&gt;</span> Container(
</span></span><span style="display:flex;"><span>    width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>    height: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>    color: Colors.red,
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// * 任何平移旋转缩放操作都能用4*4矩阵表示
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//Transform( transform: Matrix4.identity().translate(x),
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">// * 或者使用 Transform.scale缩放 / .rotate旋转(angle:0-2pi) / .translate平移
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child: Center(child: Transform.rotate(	<span style="color:#75715e">//旋转
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      angle: value, 
</span></span><span style="display:flex;"><span>      child: <span style="color:#66d9ef">const</span> Text(<span style="color:#e6db74">&#34;Hi&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">70</span>)),
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>   ),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//平移变换
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>TweenAnimationBuilder(
</span></span><span style="display:flex;"><span>  duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//tween中可以直接放置offset
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  tween: Tween(begin: <span style="color:#66d9ef">const</span> Offset(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">0</span>), end: <span style="color:#66d9ef">const</span> Offset(<span style="color:#ae81ff">40</span>,<span style="color:#ae81ff">0</span>)),
</span></span><span style="display:flex;"><span>  builder: (BuildContext context, Offset value, Widget<span style="color:#f92672">?</span> child) <span style="color:#f92672">=&gt;</span> Container(
</span></span><span style="display:flex;"><span>    width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>    height: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>    color: Colors.red,
</span></span><span style="display:flex;"><span>    child: Center(child: Transform.translate(<span style="color:#75715e">//平移操作
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      offset: value,
</span></span><span style="display:flex;"><span>      child: <span style="color:#66d9ef">const</span> Text(<span style="color:#e6db74">&#34;Hi&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">70</span>),),
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>   ),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><h3 id="15-计数器动画">1.5 计数器动画</h3>
<p>实现的效果如下：</p>
<img src="https://i.loli.net/2021/11/01/kQPf3DOgAhqXNZS.jpg" alt="6E7223F5-16FA-4D8C-AB06-57CF0EA8E594_1_201_a" style="zoom:33%;" />
<p>代码：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(<span style="color:#66d9ef">const</span> MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatelessWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>        home: Scaffold(
</span></span><span style="display:flex;"><span>            body: Center(
</span></span><span style="display:flex;"><span>                child: Container(
</span></span><span style="display:flex;"><span>      width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>      height: <span style="color:#ae81ff">120</span>,
</span></span><span style="display:flex;"><span>      color: Colors.blue,
</span></span><span style="display:flex;"><span>      child: <span style="color:#66d9ef">const</span> AnimatedCounter(<span style="color:#ae81ff">4</span>,duration: Duration(seconds: <span style="color:#ae81ff">1</span>)), <span style="color:#75715e">//使用滚动计数器
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    ))));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//滚动计数器
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">AnimatedCounter</span> <span style="color:#66d9ef">extends</span> StatelessWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">int</span> number;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">final</span> Duration duration;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> AnimatedCounter(<span style="color:#66d9ef">this</span>.number, {<span style="color:#66d9ef">this</span>.duration <span style="color:#f92672">=</span> <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>) ,Key<span style="color:#f92672">?</span> key})
</span></span><span style="display:flex;"><span>   <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> TweenAnimationBuilder(
</span></span><span style="display:flex;"><span>        duration: duration,
</span></span><span style="display:flex;"><span>        tween: Tween<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">double</span><span style="color:#f92672">&gt;</span>(end: number.toDouble()), <span style="color:#75715e">//调整end
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        builder: (context, <span style="color:#66d9ef">double</span><span style="color:#f92672">?</span> value, child) {
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">final</span> whole <span style="color:#f92672">=</span> value<span style="color:#f92672">!</span> <span style="color:#f92672">~/</span> <span style="color:#ae81ff">1</span>; <span style="color:#75715e">//整数部分
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          <span style="color:#66d9ef">final</span> decimal <span style="color:#f92672">=</span> value <span style="color:#f92672">-</span> whole;
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">return</span> Stack(children: [
</span></span><span style="display:flex;"><span>            _MyText(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">$</span>whole<span style="color:#e6db74">&#34;</span>, position: <span style="color:#f92672">-</span><span style="color:#ae81ff">100</span> <span style="color:#f92672">*</span> decimal, opacity: <span style="color:#ae81ff">1</span> <span style="color:#f92672">-</span> decimal),
</span></span><span style="display:flex;"><span>            _MyText(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>whole <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span><span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>,
</span></span><span style="display:flex;"><span>                position: <span style="color:#ae81ff">100</span> <span style="color:#f92672">-</span> decimal <span style="color:#f92672">*</span> <span style="color:#ae81ff">100</span>, opacity: decimal),
</span></span><span style="display:flex;"><span>          ]);
</span></span><span style="display:flex;"><span>        });
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_MyText</span> <span style="color:#66d9ef">extends</span> StatelessWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">String</span> text;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">double</span> position;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">double</span> opacity;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> _MyText(<span style="color:#66d9ef">this</span>.text,
</span></span><span style="display:flex;"><span>      {<span style="color:#66d9ef">required</span> <span style="color:#66d9ef">this</span>.position, <span style="color:#66d9ef">required</span> <span style="color:#66d9ef">this</span>.opacity, Key<span style="color:#f92672">?</span> key})
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> Positioned(
</span></span><span style="display:flex;"><span>      child: Opacity(
</span></span><span style="display:flex;"><span>        opacity: opacity,
</span></span><span style="display:flex;"><span>        child: Text(text, style: <span style="color:#66d9ef">const</span> TextStyle(fontSize: <span style="color:#ae81ff">100</span>)),
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>      top: position,
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
  </channel>
</rss>
