Flutter 知识点总结

2019-10-09  本文已影响0人  wayDevelop
 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 渲染过程

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)的视图


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);//注册视图工厂
}
上一篇下一篇

猜你喜欢

热点阅读