美文网首页
读书笔记『Flutter从0到1构建大前端应用』

读书笔记『Flutter从0到1构建大前端应用』

作者: cname_1 | 来源:发表于2020-05-12 17:12 被阅读0次

    第一章 Flutter简介

    P4:Flutter Engine

    • Skia:2D渲染引擎(Android系统自带,iOS系统不自带,因此iOS包所占用的存储空间更大)
    • Dart:Dart运行时
    • Text:文本排版引擎

    第二章 Dart 语言入门

    P18:Dart是一门强类型语言,在第一次赋值时,如果已经确定了是字符串类型,则不能更改为别的类型。如果真的想要改变,可以使用dynamic关键字。
    P19:num声明的变量可以加入的是int型,还可以被改成double型,但是反过来int声明的变量不能再赋值为double。

    num a = 10;
    a = 30.2;
    

    P23:使用bool表示布尔值,布尔值只有true和false;可以在debug模式下通过assert断言判断布尔值

    var a = '';
    assert(a.isEmpty);
    

    创建一个不可变数组,使用const[...]

    var list = const[1,2,3];
    

    P25:<font color="#dd0000">使用dynamic时会告诉编译器,我们不用做类型检测,并且知道自己在做什么。</font>
    P26:可以使用as,is关键字对类型进行检测

    dynamic obj = <String,int>();
    if(obj is Map<String,int>) {
      obj['age'] = 20;
    }
    

    <font color="#dd0000">~/ 除法,返回一个整数结果</font>

    级联操作符:有点类似一些语言的链式调用。

    String s = new StringBuffer()
        ..write('a')
        ..write('b').toString();
    

    P28-29:函数Function
    可选参数,可选的命名参数,即不传这些参数也可以。

    void userSettings({int age,String name}) //可选参数使用{}包括
    

    必传参数,使用@required修饰

    void userSettings({@required int age,String name})
    

    可选的位置参数

    void userSettings({int age,String name,[String interests]}) {
      if(interests != null) {
        //...
      }
    }
    

    默认参数,默认值是编译时常量

    void userSettings({int age = 21,String name='小米'})
    

    P31:一个Future表示一个异步操作产生的的结果。

    Future<int> future = getFuture();
    future.then((value) => handleValue(value)) //then用于接受异步处理的结果
      .catchError((error) => handleError(error)) //catchError用于捕获异常
      .whenComplete() => handleComplete(); //whenComplete用于处理执行结束的回调,无论是否异常都会执行
    

    then用于接受异步处理的结果;catchError用于捕获异常;whenComplete用于处理执行结束的回调,无论是否异常都会执行
    当有需要有延迟的运算(async)时,可以将其放到队列(await)中去,注意:<font color="#dd0000">await必须在async标记的函数中运行,否则会报错。</font>async和await用于解决嵌套回调等问题:

    steps() async {
      try {
        String step1Res = await step1('step1');
        String step2Res = await step2(step1Res);
        String step3Res = await step1(step2Res);
      } catch(e) {}
    }
    

    P32Flutter的继承也是单继承的(extends),因为Flutter没有访问修饰符,所以子类可以访问父类中所有的变量和方法;<font color="#dd0000">Flutter中每个类都是一个隐式的接口,这个接口包含类中所有的成员变量和方法</font>。当类被当做接口使用时,类中的方法需要在子类中被实现。
    <font color="#dd0000">P34:</font>Dart新增的mixins语法特性,作用是可以在类中混入其他功能。具体的讲,就是把<font color="#dd0000">自己的方法提供给其他类使用,但却不需要成为其他类的父类,它以非继承的方式来复用代码</font>。使用mixins,需要使用with关键字。

    abstract class CanFixComputer {
      ...
      void fixComputer() {
        print('软件工程师修电脑')
      }
    }
    class ITTeacher with CanFixComputer {
      @override
      void fixComputer() {
        print('IT老师修电脑')
      }
    }
    

    第三章 一切皆组件

    P40:从布局来看,官方把Flutter布局分为Basic Widget、Single-Child、Multi-Child。
    P58:Scaffold是基于
    Material库的一个与路由相关**的模板组件,可以认为是Flutter提供的标准化的布局容器。

    Widget build(BuildContext context) {
      return Scaffold(
        AppBar:...
        body:...
        bottomNavigationBar:...
        floatingActionButton:...
        drawer:...
        ...
      )
    }
    

    P62:Column是不支持滚动的,如果需要实现滚动功能,则需要考虑使用ListView。
    P76:使用Wrap替换Row可以实现自动换行的效果。
    P79:Context表示组件上下文的意思。通过Context可以遍历和查找当前Widget树
    <font color="#dd0000">P80:</font>StatelessWidget即无状态的Widget,它<font color="#dd0000">无法通过setState设置组件的状态</font>。对于其内部属性,应该被声明为final,防止被意外修改。

    class MyStatelessWidget extends StatelessWidget {
      MyStatelessWidget({
        Key key,
        this.parameter,
      }):super(key:Key),
      final parameter;
      @override
      Widget build(BuildContext context){}
    }
    

    StatefulWidget即有状态Widget。创建一个StatefulWidget组件时,会同时创建一个State对象,并且StatefulWidget通过与State关联可以达到刷新UI的目的。

    class MyStatefulWidget extends StatefulWidget {
      MyStatefulWidget({
        Key key,
        this.color,
      }):super(key:key),
      
      final Color color;
     
      @override 
      _MyStatefulWidgetState createState() => new _MyStatefulWidgetState();
    }
    
    class _MyStatefulWidgetState extends State<MyStatefulWidget> {
      ...
      @override
      Widget build(BuildContext context) {...}
    }
    

    P82:生命周期
    initState是State生命周期里第一个被执行的方法,可以在该方法进行一些初始化动作。<font color="#dd0000">在initState里,Framework层还没有把Context和State关联在一起,所以还不能访问Context。初始化方法在生命周期里只会被执行一次。</font>

    didChangeDependencies:在执行完initState之后就可以访问Context了。如果Widget使用了InheritedWidget的数据,并且在InheritedWidget的数据发生改变时,Flutter Framework就会触发didChangeDependencies的回调。
    build:在执行完didChangeDependencies之后,<font color="#dd0000">每次调用setState都会触发build方法。</font>
    dispose:在组件被销毁时调用。

    img
    <font color="#dd0000">P88</font>:在Flutter中,每个Widget都有一个唯一标识Key,在创建和渲染时生成,可以手动指定。在某些场景下,需要保存Key,可以保存到GlobalKey、LocalKey、UniqueKey、ObjectKey中,可以根据key找到的对应的Widget。
    P89:被InheritedWidget暴露出来的数据,可以高效地在Widget树中从上往下传递和共享,并支持跨级数据传递。

    第四章 事件处理

    P100:原始指针事件,把Listener包裹在需要监听的组件外来实现。

    ...
      body:Center(
        child:Listener(
            child:Container()
        )
      )
    

    P102:可以使用IgnorePoiner和AbsorbPointer对事件进行忽略,前者本身可以接受事件,后者本身也不可接收事件。
    P105:GestureDetector可以支持更丰富的事件,如缩放、双击、垂直、水平的手势。同样需要包裹在要监听的组件外面。

    GestureDetector (
        onTap:() {
        print('tap');
      },
      child:Container()...
    )
    

    P112:事件竞争和手势冲突
    Flutter加入了手势竞技场的概念。在给同一个组件同时添加水平和垂直回调时,若用户将指针水平移动超过一定的逻辑像素,则判定水平获胜。反之判定垂直获胜。

    第五章 动画

    P129:当创建AnimationController时,需要传入vsync参数,这个参数接受的是TickerProvider类型的对象,<font color="#dd0000">作用是阻止在屏幕锁屏时执行动画以避免不必要的资源浪费。</font>
    P136:AnimatedWidget对addListener、setState等动画相关动作进行了封装,使用起来更方便。
    P141:通过Hero,可以在路由之间做出流程的转场动画。Hero的组件需要同时定义源组件和目标组件,其中源组件和目标组件被Hero包裹在需要动画控制的组件外面。

    第六章 使用网络技术和异步编程

    P184:在Flutter里,异步使用Futrue来修饰的,并运行在event loop里。Flutter中一个很重要的概念是isolate,通过Flutter Engine层面的一个线程来实现的,而实现isolate的线程又是由Flutter管理和创建的。所有的Dart代码都运行在isolate上。通常情况下,我们的应用都运行在main isolate中,在有必要时,我们可以创建新的isolate。<font color="#dd0000">多个isolate无法共享内存,必须通过相关的API通信才可以。</font>
    P185:event loop

    img
    1. 运行APP并执行main方法
    2. 开始ging优先处理microtask queue,知道队列为空
    3. 当microtask queue为空后,开始处理event queue。如果event queue里面有event,则执行,<font color="#dd0000">每执行一条再判断此时新的microtask queue是否为空</font>,并且每次只取出一条来执行。可以这么理解,在处理所有event之前,我们会做一些市区内给,并且会把这些事情放在microtask queue中。
    4. microtask queue 和 event queue都为空,则APP可以正常退出。
      注意:当处理microtask queue时,event queue会被阻塞,所以microtask queue中应避免任务太多或长时间处理,否则将导致APP的绘制和交互等行为被卡住
      P192:Future表示“将来”一次异步获取得到的数据,而Stream是多次异步获取得到的数据;Future将返回一个值,而Stream将返回多次值。

    第七章 路由

    P197:一个界面跳转到另一个界面使用Navigator.push方法,返回上一个界面使用Navigator.pop方法。路由分为静态路由和动态路由。
    204:参数回传

    1. 等待回传数据
        _navigateAndDisplaySelection(BuildContext context) async {
          final result = await Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => SelectionScreen()),
          );
        }
      
    2. 回传数据
      Navigator.pop(context, 'Flutter');
      

    P207:路由栈
    类似Android的launchMode功效

    • pushReplacementNamed:替换当前栈顶页面为新的页面
    • popAndPushNamed:和上面的一样,只是多了pop的交互效果
    • pushNamedAndRemoveUntil:添加一个元素,并且移除历史,直到named对象
    • popUnil:没有push,移除历史,直到named对象

    第八章 持久化

    P215:推荐使用shared_preferences插件,这是一个异步的key-value存储的插件。

    • 写入
      SharedPreferences prefs = await SharedPreferences.getInstance();
      prefs.setString(k,v);
      
    • 读取
      SharedPreferences prefs = await SharedPreferences.getInstance();
      prefs.getString(k);
      
    • 清除
      SharedPreferences prefs = await SharedPreferences.getInstance();
      prefs.remove(k);
      

    P220:sqflite

    • 支持事务和批处理
    • 支持自动version管理
    • 支持增、删、改、查的Helper工具类
    • 支持Android/iOS后台线程的运行
      获取和删除
    var databasesPath = await getDatabasesPath();
    String path = join(databasePath,'demo.db');
    await deleteDatabase(path);
    

    打开并创建数据库

    Database database = await openDatabase(path,version:1,oncreate:(
        Database db,int version) async {
        await db.execute('CREATE TABLE Test(id INTEGER ...)')
        }
    ));
    

    插入数据的两种方式
    //方式一,直接使用sql

    Future<int> rawInsert(String sql,[List<dynamic> arguments]);
    int id = await txn.rawInsert(
        'INSERT INTO Test(name,value,num) VALUES(?,?,?)',['another name',1213,3.1212])
    );
    

    //方式二,使用map

    Future<int> insert(String table,Map<String,dynamic> values,{String nullColumnHack,ConflictAlgorithm conflictAlgorithm});
    

    修改的两种方式,和插入一样,只是方法名变了

    Future<int> rawUpdate(String sql,[List<dynamic> arguments]);
    
    Future<int> rawUpdate(String table,Map<String,dynamic> values,{String nullColumnHack,ConflictAlgorithm conflictAlgorithm});
    

    查询的两种方式

    Future<List<Map<String,dynamic>>> rawQuery(String sql,[List<dynamic> arguments]);
    
    Future<List<Map<String,dynamic>>> Query(String table,{
      bool distinct,List<String> columns,String where,List<dynamic> whereArgs,
      String groupBy,String having,String orderBy,int limit,int offset
    });
    

    物理删除的两种方式

    Future<int> rawDelete(String sql,[List<dynamic> arguments]);
    
    Future<int> delete(String table,{String where,List<dynamic> whereArgs})
    

    计算总记录数

    Sqflite.firstIntValue(await database.rawQuery('SELECT COUNT(*) FROM Test'))
    

    关闭数据库

    await databse.close()
    

    第九章

    P238:添加未发布的package,编辑pubspec.yaml

    dependencies:
        plugin1:
            path: ../plugin1/
    

    添加git上的依赖

    dependencies:
        plugin1:
            git:
                url: git://github.com/flutter/plugin1.git
    

    P239:调用flutter packages get之后,会生成pubspec.lock。该文件确保更新的package不会影响现有代码。
    P240:创建自己的package

    flutter create --org com.example --template=plugin -i swift -a kotlin hello
    

    --org:指定包名;-i:指定iOS语言;-a:指定Android语言
    P241:Platform Channel
    Platform Channel 是Flutter与Platform指定的通信机制,包括3种

    1. BasicMessageChannel:用于传递字符串和半结构化的信息(在大内存数据块传递的情况使用)
    2. MethodChannel:用于传递方法的调用
    3. EventChannel:用于数据流(event streams)的通信
      P242:Flutter的消息传递工具是BinaryMessager,传递的消息格式是二进制的。二进制格式的消息通过消息编解码器(Codec)解码为能识别的消息,并传递给Handler来进行处理。
      P253:在Android里每初始化一个FlutterView就会有一个FlutterEngine被初始化,这样就会有新的线程在Dart上运行。如果每个Activity中都有一个FlutterView,则会创建多个Flutter Engine实例,这会导致每个Flutter Engine实例加载的代码都独立运行在ioslate中,这种模式被称为多引擎模式。存在问题
    4. 冗余的资源问题。
    5. 插件注册问题。插件依赖Messenger传递消息,多个FlutterView时,插件的注册和通信将变得混乱和难以维护。
    6. Flutter Widget和Native页面的差异化问题。
    7. 增加页面之间通信的复杂度。
      P254:FlutterBoost的思想是把Flutter容器做成浏览器的样子,然后填写一个页面地址,再由容器去管理页面的绘制。在Native端,如果初始化容器,就设置容器对应页面的标志即可。

    相关文章

      网友评论

          本文标题:读书笔记『Flutter从0到1构建大前端应用』

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