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

第一章 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使用dynamic时会告诉编译器,我们不用做类型检测,并且知道自己在做什么。
P26:可以使用as,is关键字对类型进行检测

dynamic obj = ();
if(obj is Map) {
  obj['age'] = 20;
}

~/ 除法,返回一个整数结果

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

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

then用于接受异步处理的结果;catchError用于捕获异常;whenComplete用于处理执行结束的回调,无论是否异常都会执行
当有需要有延迟的运算(async)时,可以将其放到队列(await)中去,注意:await必须在async标记的函数中运行,否则会报错。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没有访问修饰符,所以子类可以访问父类中所有的变量和方法;Flutter中每个类都是一个隐式的接口,这个接口包含类中所有的成员变量和方法。当类被当做接口使用时,类中的方法需要在子类中被实现。
P34:Dart新增的mixins语法特性,作用是可以在类中混入其他功能。具体的讲,就是把自己的方法提供给其他类使用,但却不需要成为其他类的父类,它以非继承的方式来复用代码。使用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树
P80:StatelessWidget即无状态的Widget,它无法通过setState设置组件的状态。对于其内部属性,应该被声明为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 {
  ...
  @override
  Widget build(BuildContext context) {...}
}

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

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

img

P88:在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类型的对象,作用是阻止在屏幕锁屏时执行动画以避免不必要的资源浪费。
P136:AnimatedWidget对addListener、setState等动画相关动作进行了封装,使用起来更方便。
P141:通过Hero,可以在路由之间做出流程的转场动画。Hero的组件需要同时定义源组件和目标组件,其中源组件和目标组件被Hero包裹在需要动画控制的组件外面。

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

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

img

  1. 运行APP并执行main方法
  2. 开始ging优先处理microtask queue,知道队列为空
  3. 当microtask queue为空后,开始处理event queue。如果event queue里面有event,则执行,每执行一条再判断此时新的microtask queue是否为空,并且每次只取出一条来执行。可以这么理解,在处理所有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 rawInsert(String sql,[List arguments]);
int id = await txn.rawInsert(
    'INSERT INTO Test(name,value,num) VALUES(?,?,?)',['another name',1213,3.1212])
);

//方式二,使用map

Future insert(String table,Map values,{String nullColumnHack,ConflictAlgorithm conflictAlgorithm});

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

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

查询的两种方式

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

物理删除的两种方式

Future rawDelete(String sql,[List arguments]);
Future delete(String table,{String where,List 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构建大前端应用』)