Flutter踩坑记

1,088 阅读6分钟
  1. Flutter_swiper 异步请求显示banner时,初始url为空时 报错问题

    参考:github.com/best-flutte…

    主要:没有请求到数据的时候不要设置自动播放。autoplay: _imageUrls.isNotEmpty

  2. 可滚动网格布局 品字型布局

    参考:CustomScrollView 组件

  3. flutter 子组件占视口100%

    参考:MediaQuery.of(context).size.width,

  4. flutter 组件理解

    参考:MaterialApp() 相当于 html 的根组件; Scaffold() 相当于 body

  5. flutter 组件声明周期

    参考官方

  6. flutter json解析工具 json_model 的使用

    主要:

  7. []. 运算符,与 js 略有不同

    参考:www.dartcn.com/guides/lang…

    主要: [] Refers to the value at the specified index in the list。 . Refers to a property of an expression; example: foo.bar selects property bar from expression foo

  8. 点击事件 GestureDetector 范围小

    参考:blog.csdn.net/cpcpcp123/a…

    主要:behavior: HitTestBehavior.opaque,属性,可以让点击事件透过这个Text的区域。如果不添加这个属性,那么只能点击到文字时才会有响应。

  9. 路由 带参数返回; Navigator.push 是一个异步操作

    参考:参考源码

    主要:page1页面需要page2页面返回数据,page1在跳转时需要等待page2返回数据

    /// page1 跳转到 page2
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) =>
        Page2(params: '来自page1'),
      ),
    ).then((data) { /// 关键:这里 data 是page2 中返回的数据
      print(data.toString());
    });
      
    /// page2 返回 page1
    Navigator.pop(context, '来自page2');
    
  10. Widget 声明周期

    /// StatefullWidget vs StatelessWidget
    
    
    StatelessWidget:无中间状态变化的widget,需要更新展示内容就得通过重新new,flutter推荐尽量使用StatelessWidget
    StatefullWidget:存在中间状态变化,那么问题来了,widget不是都immutable的,状态变化存储在哪里?flutter 引入state的类用于存放中间态,通过调用state.setState()进行此节点及以下的整个子树更新
    State 生命周期
    
    
    initState(): state create之后被insert到tree时调用的
    didUpdateWidget(newWidget):祖先节点rebuild widget时调用
    deactivate():widget被remove的时候调用,一个widget从tree中remove掉,可以在dispose接口被调用前,重新instert到一个新tree中
    didChangeDependencies():
    初始化时,在initState()之后立刻调用
    当依赖的InheritedWidget rebuild,会触发此接口被调用
    
    
    build():
    After calling [initState].
    After calling [didUpdateWidget].
    After receiving a call to [setState].
    After a dependency of this [State] object changes (e.g., an[InheritedWidget] referenced by the previous [build] changes).
    After calling [deactivate] and then reinserting the [State] object into the tree at another location.
    
    
    dispose():Widget彻底销毁时调用
    reassemble(): hot reload调用
    注意事项:
    
    
    
    A页面push一个新的页面B,A页面的widget树中的所有state会依次调用deactivate(), didUpdateWidget(newWidget)、build()(这里怀疑是bug,A页面push一个新页面,理论上并没有将A页面进行remove操作),当然从功能上,没有看出来有什么异常
    当ListView中的item滚动出可显示区域的时候,item会被从树中remove掉,此item子树中所有的state都会被dispose,state记录的数据都会销毁,item滚动回可显示区域时,会重新创建全新的state、element、renderobject
    使用hot reload功能时,要特别注意state实例是没有重新创建的,如果该state中存在一下复杂的资源更新需要重新加载才能生效,那么需要在reassemble()添加处理,不然当你使用hot reload时候可能会出现一些意想不到的结果,例如,要将显示本地文件的内容到屏幕上,当你开发过程中,替换了文件中的内容,但是hot reload没有触发重新读取文件内容,页面显示还是原来的旧内容
    
  11. 数据自上而下。与双向绑定的区别。

    主要:

  12. Icons 图标库

    参考: material.io/resources/i…

  13. 常用组件:

    1. 加载: CircularProgressIndicator
    2. 下拉刷新:RefreshIndicator
    3. 事件:GestureDetector
    
  14. 吸顶滚动的实现方案

    主要:1.SliverPersistentHeade 可配合 NestedScrollView 使用

    参考:CustomScrollView 的属性 shrinkWrap: true, 会改变 SliverPersistentHeader 浮动的层级。

  15. 在 List.map() 中取到index 的方法

    参考:stackoverflow.com/questions/5…

    主要:有一种asMap方法可以将列表转换为映射,其中键是索引,值是索引中的元素。请在这里查看文档。例:

    List _sample = ['a','b','c'];
    _sample.asMap().forEach((index, value) => f);
    
    List<Widget> _buildWidgets(List<Object> list) {
        return list
            .asMap()
            .map((index, value) =>
                MapEntry(index, _buildWidget(index, value)))
            .values
            .toList();
    }
    

    或者,您可以创建一个同步生成器函数以返回一个可迭代

    Iterable<MapEntry<int, T>> enumerate<T>(Iterable<T> items) sync* {
      int index = 0;
      for (T item in items) {
        yield MapEntry(index, item);
        index = index + 1;
      }
    }
    
    //and use it like this.
    var list = enumerate([0,1,3]).map((entry) => Text("index: ${entry.key}, value: ${entry.value}"));
    
  16. list 二维展开

    参考:juejin.cn/post/684490…

    主要:List.expand()

  17. 网格布局 子元素宽高适配???

  18. tabView 缓存问题???

  19. 通常在用到 PageView + BottomNavigationBar 或者 TabBarView + TabBar 的时候大家会发现当切换到另一页面的时候, 前一个页面就会被销毁, 再返回前一页时, 页面会被重建, 随之数据会重新加载, 控件会重新渲染

    参考:juejin.cn/post/684490…

  20. 键盘弹起导致布局overflow

    flutter中关于软键盘弹起导致的问题
    
    当布局高度写死时,例如设置为屏幕高度,这时候键盘弹起页面上会出现布局overflow的提示
    软键盘弹起后遮挡输入框
    原因:在flutter中,键盘弹起时系统会缩小Scaffold的高度并重建
    
    解决问题1中overflow提示的两种办法:
    1)把Scaffold的resizeToAvoidBottomInset属性设置为false,这样在键盘弹出时将不会resize
    
    2)把写死的高度改为 原高度 - MediaQuery.of(context).viewInsets.bottom,键盘弹出时布局将重建,而这个MediaQuery.of(context).viewInsets.bottom变量在键盘弹出前是0,键盘弹起后的就是键盘的高度
    
    解决问题2的办法:
    将输入框放进可滚动的Widget中即可,当输入框获取焦点后,系统会自动将它滑动到可视区域
    
  21. TextField 焦点与hint不对齐问题

    style: TextStyle(
      fontSize: 16,
      textBaseline: TextBaseline.alphabetic,
    ),
    
  22. ScreenUtil 打 release 包的问题,大概因为ScreenUtil初始化在渲染首页之后,导致界面文字或字体显示不全

    参考:github.com/OpenFlutter…

    主要:两种解决方案

    之前有人在官方issue里提过,官方更推荐使用MediaQuery, window获取数据不依赖Widget的生命周期,这就会产生一些问题,例如使用debug版本因为程序运行缓慢而拿到宽高数据,而release版本应用速度提升后就无法获取。我现在是在iniState中使用SchedulerBinding.instance.addPostFrameCallback()的回调进行初始化,希望对您有帮助。
    
    @hzysunrain 我试过在didChangeMetrics中初始化ScreenUtil , 但是它的触发时间晚于build (在我尝试的几次)
    哪怕我在Myapp中的didChangeMetrics都要在home的build方法之后才调用
    
    比较理想的就是在home(一般作为启动页面)中初始化 , 但是不使用ScreenUtil(避免特殊情况拿不到屏幕数据), 在启动页的下一个页面应该就可以直接使用了
    

23.使用SingleChildScrollView和Column可以使页面滚动,Column中有ListView时,为使ListView滚动和页面滚动不冲突,可设置ListView的physics为NeverScrollableScrollPhysics,SingleChildScrollView的physics为AlwaysScrollableScrollPhysics。

24.ListView,GridView,TabBarView等Widget都没有自己的高度,必须指定父组件高度,若在Column中想设置动态高度可用Expanded嵌套。