美文网首页
Flutter 知识点总结

Flutter 知识点总结

作者: wayDevelop | 来源:发表于2019-10-09 13:56 被阅读0次
     MainAxisAlignment.spaceBetween
    将主轴空白位置进行均分,排列子元素,首尾子控件距边缘没有间隙
    MainAxisAlignment.spaceAround
    将主轴空白区域均分,使中间各个子控件间距相等,首尾子控件距边缘间距为中间子控件间距的一半
    MainAxisAlignment.spaceEvenly
    将主轴空白区域均分,使各个子控件间距相等
    

    flutter column row布局的列表自适应宽高
    mainAxisSize: MainAxisSize.min

    通过runtimeType可以获取当前数据类型
    var e = [12.5,13.1];
    print('e 的类型是: ${e.runtimeType}'); // e 的类型是: List<double>

    flutter column嵌套listview不能滚动,或者不显示的问题

    因为 listview水平视口的宽度是无限的。
    在listview外面嵌套一个expanded,或者一个container就可以了,尺寸计算的问题,expande就是listview有多大就有多大,container就是container多大listview就有多大,可以滚动

     child: Row(
              children: <Widget>[
                Expanded(
                  child: ListView.builder(
                    shrinkWrap: true,
                    scrollDirection: Axis.horizontal,
                    physics: BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
                    itemCount: 120,
                    itemBuilder: (context, index) => Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: new Text('${index}'),
                    ),
                  ),
                ),
              ],
            ),
    

    List 常见用法

    
       List<dynamic> topTitles = ['审批单', '机票列表', '客服'];
        //遍历list
        for(var value intopTitles){
          print("---------<>---${value}");
        }
        topTitles.forEach((item) => {print(item)});
    
    
    //遍历得到一个新的List  用此方法 亦可动态加载Widget
       List<dynamic>   newList = topTitles.map((item) {
          return item + "111";
        }).toList();
    
    
    //list的map方法不能获取index  用Asmap  转换成map 后
    //动态加载Widget
    
            Row(
                    children: topTitles
                        .asMap()
                        .keys
                        .map((index) => Expanded(
                              flex: 1,
                              child: Column(
                                children: <Widget>[Text(topTitles[index])],
                              ),
                            ))
                        .toList(),
                  ),
    
    
    
    

    Map

    
        //遍历map
     mostCare.forEach((k, v) {
          // print(k + "==" + v.toString()); //类型不一样的时候就toString()
        });
    
    
    
    //map   key遍历生成Widget
     Row(
                    children: mostCare.keys.map((key) {
                      //  print('-----key--${key}');
                      return Expanded(
                        flex: 1,
                        child: Column(
                          children: <Widget>[Text(mostCare[key])],
                        ),
                      );
                    }).toList(),
                  ),
    

    延迟

    /*    //延迟3秒
        Future.delayed(Duration(seconds: 3), () {
         
        });*/
    

    软键盘弹出顶掉内容、防止键盘超出屏幕

    研究了半天发现十分简单,只需两行代码。布局中最外层包裹一个SingleChildScrollView组件,然后在Scaffold里增加一个属性 resizeToAvoidBottomPadding: false,即刻解决键盘遮挡问题,
    类似于 Android 中的 android:windowSoftInputMode=”adjustResize”,控制界面内容 body 是否重新布局来避免底部被覆盖了,比如当键盘显示的时候,重新布局避免被键盘盖住内容。默认值为 true。

    Flutter TextField 光标和内容不能对齐问题

    添加该属性
    TextField(
        style: TextStyle(textBaseline: TextBaseline.alphabetic),
    )
    
    

    flutter中text设置overflow还是会超出屏幕解决方法

    使用flex控件代替row控件,并且在文字外面包一层Expanded
    ,应该是横向row没有确定宽度,text根据内容来撑开row,所以就会超出,换成flex 使text最大宽度能占用剩下的所有宽度,所以达到最宽的时候就会显示省略号。
    这个错误就好像再column中使用listView一样,会出现一个在无限高度的view中使用listView的错误,

    屏幕适配原理

    flutter_screenutil:

    说一下适配方案, 比如我们设计师设计的UI是根据Iphone6来做的,我们知道 iPhone6的分辨率是750*1334(px)、

    又或者是根据hdpi的设备来设计的UI,我们知道hdpi的 Android设备是 (240 dpi),像素密度是1.5,即hdpi设备的分辨率宽度是320px, 总之,无论设计稿的单位是px,或者是dp,我们都能够转换成px.
    那么我们如果根据px来适配,ios和 android 就都可以兼容了.
    假设,我们的设计稿手机是10801920 px.
    设计稿上有一个540960 的组件, 即宽度和宽度是手机的一半. 如果我们直接写的时候组件的尺寸这么定义,在其他尺寸的设备上未必是一半,或多,或少. 但是我们可以按比例来看,即我们要实现的宽度是实际设备的一半.
    那么假设我们设备的宽度是deviceWidth和deviceHeight , 我们要写的组件大小为: 宽:(540/1080)deviceWidth,高度: (960/1920)deviceHeight.
    通过这个公式我们可以发现,我们要写的组件宽度就是设计稿上的尺寸width(deviceWdith/原型设备宽度).那么每次我们写ui的时候,只要直接哪来设计稿的尺寸(deviceWdith/设备原型)宽度即可
    原理就是先获取,实际设备与原型设备的尺寸比例.

    构造方法

    如果没有自定义构造方法,则会有个默认构造方法
    如果存在自定义构造方法 默认构造方法将失效
    构造方法不能重载

    命名构造方法
    使用命名构造方法 可以实现多个构造方法
    使用 类名.方法 的形式实现

    class Person {
        string name
        int age
        final string sex
        /**
         * 这里 使用this的语法糖可以对final类型赋值
         * 但是写在构造方法中就不行了,因为执行在构造方法之前
         */
        Person (string name ,this.age ,this.sex) { // 第二种是语法糖,
            this.name = name
            print(age)
        }
    }
    
    var per = new Person('jack', 20)
    
    ---------------------------------------
    命名构造方法
    class Person {
        string name
        int age
        final string sex
    
        Person (string name ,this.age ,this.sex) { // 第二种是语法糖,
            this.name = name
            print(age)
        }
        Person.withName(string name) {
            this.name = name
        }
    }
    <!-- 多个构造方法 类似oc -->
    new Person('jack', 12, 'man')
    new Person.withName('marry')
    
    

    混合 mixins (with)

    除了继承和接口实现之外,Dart 还提供了另一种机制来实现类的复用,即“混入”(Mixin)。通过混入,一个类里可以以非继承的方式使用其他类中的变量与方法,效果正如你想象的那样。

    混合的对象是类
    可以混合多个

    生命周期

    State 的生命周期可以分为 3 个阶段。
    State 初始化时会依次执行 :构造方法 -> initState -> didChangeDependencies -> build,随后完成页面渲染。
    Widget 的状态更新,主要由 3 个方法触发:setStState、didchangeDependencies 与 didUpdateWidget。
    Widget 组件销毁相对比较简单,系统会调用 deactivate 和 dispose 这两个方法,来移除或销毁组件。

    Widget 渲染过程

    • Widget、是控件实现的基本逻辑单位,里面存储的是有关视图渲染的配置信息,包括布局、渲染属性、事件响应信息等。
    • Element :是 Widget 的一个实例化对象,它承载了视图构建的上下文数据,是连接结构化的配置信息到完成最终渲染的桥梁。
    • RenderObject 、主要负责实现视图渲染的对象、通过控件树中的每个控件创建不同类型的渲染对象,组成渲染对象树。

    Fluttter 将视图树的概念进行了扩展,把视图数据的组织和渲染抽象为三部分,即 Widget,Element 和 RenderObject。
    渲染对象树在 Flutter 的展示过程分为四个阶段,即布局、绘制、合成和渲染。
    布局和绘制在 RenderObject 中完成,Flutter 采用深度优先机制遍历渲染对象树,确定树中各个对象的位置和尺寸,并把它们绘制到不同的图层上,布局和绘制完成后,再交给 Skia进行合成和渲

    为什么需要增加中间的这层 Element 树呢?直接由 Widget 命令 RenderObject 去干活儿不好吗?

    因为 Widget 具有不可变性,但 Element 却是可变的,实际上,Element 树这一层将 Widget 树的变化(类似 React 虚拟 DOM diff)做了抽象,可以只将真正需要修改的部分同步到真实的 RenderObject 树中,最大程度降低对真实渲染视图的修改,提高渲染效率,而不是销毁整个渲染视图树重建。

    合成和渲染

    随着页面越来越复杂、Flutter 的渲染树层级很多,直接交付给渲染引擎进行多图层渲染,可能会出现大量渲染内容的重复绘制,所以还需要先进行一次图层合成,即将所有的图层根据大小、层级、透明度等规则计算出最终的显示效果,将相同的图层归类合并,简化渲染树,提高渲染效率,合并完成后,Flutter 会将几何图层数据交由 Skia 引擎加工成二维图像数据,最终交由 GPU 进行渲染,完成界面的展示。

    StatelessWidget与StatefulWidget区别

    分别是组装控件的容器
    StatelessWidget 不带绑定状态,而 StatefulWidget 带绑定状态,其依赖的数据在 Widget 生命周期中可能会频繁地发生变化,由 State创建视图,数据驱动视图更新。

    单线程模型

     // 声明了一个延迟 3 秒返回的 Hello Flutter   的 Future,
        Future<String> fetchContent() async {
          await Future.delayed(new Duration(seconds: 3));
          return 'Hello Flutter';
        }
    
        /**在Dart中,有await标记的运算,其结果值都是一个Future对象,
         * 对于异步函数返回的 Future 对象,如果调用者决定同步等待, 则需要在调用处使用 await 关键字,
         * 并且在调用处的函数体使用 async 关键字。
         * Dart 中的 await 并不是阻塞等待,而是异步等待,Dart 会将调用体的函数也视作异步函数,
         * 将等待语句的上下文放入 Event Queue 中,一旦有了结果,Event Loop 就会把它从 Event Queue 中取出,等待代码继续执行。
         */
        //await 与 async 有效区间只对调用上下文的函数有效,并不向上传递
    
    
        testAwaitAndAsync() async {
          String data = await fetchContent();
          print('-----${data}-----');  //等待3秒后打印Hello Flutter   然后打印123
          print('----123------');
        }
    
    

    Event Loop 完整版的流程图
    在 Dart 中,实际上有两个队列,一个事件队列(Event Queue),另一个则是微任务队列(Microtask Queue)。在每一次事件循环中,Dart 总是先去第一个微任务队列中查询是否有可执行的任务,如果没有,才会处理后续的事件队列的流程。


    Isolate

    Dart 也提供了多线程机制,即 Isolate。在 Isolate 中,资源隔离做得非常好,每个 Isolate 都有自己的 Event Loop 与 Queue,Isolate 之间不共享任何资源,只能依靠消息机制通信,因此也就没有资源抢占问题。

    Isolate 通过发送管道(SendPort)实现消息通信机制。我们可以在启动并发 Isolate 时将主 Isolate 的发送管道作为参数传给它,这样并发 Isolate 就可以在任务执行完毕后利用这个发送管道给我们发消息了。
    ,在 Isolate 中,发送管道是单向的:我们启动了一个 Isolate 执行某项任务,Isolate 执行完毕后,发送消息告知我们。如果 Isolate 执行任务时,需要依赖主 Isolate 给它发送参数,执行完毕后再发送执行结果给主 Isolate,这样双向通信的场景我们如何实现呢?答案也很简单,让并发 Isolate 也回传一个发送管道即可。

    跨组件通讯

    Flutter 与 Android iOS 原生的通信有以下三种方式

    BasicMessageChannel 实现 Flutter 与 原生(Android 、iOS)双向通信 ,主要是传递字符串json等数据和一些半结构体的数据,
    MethodChannel 实现 Flutter 与 原生原生(Android 、iOS)双向通信, 用于传递方法调用
    EventChannel 实现 原生原生(Android 、iOS)向Flutter 发送消息 ,用于数据流(event streams)的通信

    平台视图 Flutter端使用原生视图

    Flutter 提供了一个平台视图(Platform View)的概念。它提供了一种方法,允许开发者在 Flutter 里面嵌入原生系统(Android 和 iOS)的视图

    • 首先,由作为客户端的 Flutter,通过向原生视图的 Flutter 封装类(在 iOS 和 Android 平台分别是 UIKitView 和 AndroidView)传入视图标识符,用于发起原生视图的创建请求;
    • 然后,原生代码侧将对应原生视图的创建交给平台视图工厂(PlatformViewFactory)实现;
    • 最后,在原生代码侧将视图标识符与平台视图工厂进行关联注册,让 Flutter 发起的视图创建请求可以直接找到对应的视图创建工厂。
    
    class SampleView extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        //使用Android平台的AndroidView,传入唯一标识符sampleView
        if (defaultTargetPlatform == TargetPlatform.android) {
          return AndroidView(viewType: 'sampleView');
        } else {
          //使用iOS平台的UIKitView,传入唯一标识符sampleView
          return UiKitView(viewType: 'sampleView');
        }
      }
    }
    
    我们分别创建了平台视图工厂和原生视图封装类,并通过视图工厂的 create 方法,将它们关联起来
    
    
    //视图工厂类
    class SampleViewFactory extends PlatformViewFactory {
        private final BinaryMessenger messenger;
        //初始化方法
        public SampleViewFactory(BinaryMessenger msger) {
            super(StandardMessageCodec.INSTANCE);
            messenger = msger;
        }
        //创建原生视图封装类,完成关联
        @Override
        public PlatformView create(Context context, int id, Object obj) {
            return new SimpleViewControl(context, id, messenger);
        }
    }
    //原生视图封装类
    class SimpleViewControl implements PlatformView {
        private final View view;//缓存原生视图
        //初始化方法,提前创建好视图
        public SimpleViewControl(Context context, int id, BinaryMessenger messenger) {
            view = new View(context);
            view.setBackgroundColor(Color.rgb(255, 0, 0));
        }
        
        //返回原生视图
        @Override
        public View getView() {
            return view;
        }
        //原生视图销毁回调
        @Override
        public void dispose() {
        }
    }
    
    
    protected void onCreate(Bundle savedInstanceState) {
      ...
      Registrar registrar =    registrarFor("samples.chenhang/native_views");//生成注册类
      SampleViewFactory playerViewFactory = new SampleViewFactory(registrar.messenger());//生成视图工厂
    
    registrar.platformViewRegistry().registerViewFactory("sampleView", playerViewFactory);//注册视图工厂
    }
    

    相关文章

      网友评论

          本文标题:Flutter 知识点总结

          本文链接:https://www.haomeiwen.com/subject/zyfgpctx.html