Flutter学习总结1:flutter的特点

发布于 2019-11-29  约 8 分钟

Flutter学习总结1:flutter的特点

学习Flutter也差不多两个月了,工作中一直没用上,不过还是挤时间做了个简陋的App练手。

项目是jf_reader,一个小说阅读器,代码开源在github。用户可以输入一个TXT文件或Zip文件的链接,应用会去下载并解析;也可以输入小说名进行搜索,应用会去一些第三方小说网站抓取。

整个项目dart代码量在1w行出头,算是对Flutter的开发有了基本的了解,这里对Flutter涉及的知识体系做个梳理。


对大部分尤其是国内的客户端开发者而言,dart的异步编程、并发编程,Flutter的声明式UI都是比较新颖的编程范式/理念,需要适应一下。其它的大部分都是API的区别,对稍有经验的开发者不算太大问题。

异步编程

原始的异步编程常用方法是回调函数,近几年来async/await这样的协程语法逐渐流行起来,dart也用了async/await。

前端开发者对此应该很熟悉了,但我发现很多优秀的客户端开发者对此心存疑虑难以接受,这里我要强调一点,async/await是目前为止毫无疑问的异步编程最佳语法,必须要学的,越早接受越好。

给个例子:

Future<Response> fetchPost() async {
  return http.get('https://jsonplaceholder.typicode.com/posts/1');
}
Future<String> loadData() async {
  Response response = await fetchPost();
  return response.body;
}

具体内容不展开了,推荐几篇文章作参考:

flutter入门:线程,异步,声明式UI

Asynchronous programming: futures, async, await

并发编程

dart使用的并发机制称为isolate。isolate是跟线程类似的运行实体,但isolate间不能共享内存,只通过消息通信,这就避免了多线程编程中令人痛苦的线程安全问题。而代价是,为了保证不共享内存,isolate间的消息是经过深拷贝的。抽象点讲,也就是通过增加一定的冗余来降低耦合。

消息和共享内存一直是并发编程的两大流派,基于共享内存的多线程派一直以来更加广泛,但近几年基于消息的并发编程范式发展非常迅速,如go/dart等比较新的语言都采用了这种方式(js的webworker其实也是)。

给个例子:

void main() async{
  final ReceivePort resultPort = ReceivePort();
  await Isolate.spawn(isolateTask,resultPort.sendPort)
  resultPort.listen((data){
    print("$data,time:${DateTime.now()}");
    resultPort.close();
  });
  print("start isolate task");
}
void isolateTask(SendPort port) async {
  await Future.delayed(Duration(seconds:5));
  port.send("isolate task done");
}

可以看到通过消息进行通信还是很麻烦的,因此flutter在此之上封装了一个compute方法,看个例子:

static Future<List<Map<String,String>>> parseWithContent (String content) async {
    List<Map<String,String>> chapters = await compute(parseContentLogic, content);
    print('chapters count:${chapters.length}');
    return chapters;
}

这里parseContentLogic是个耗时操作,content是要传进去的参数,compute就会开个isolate去执行并返回一个future对象了。

推荐几篇文章:

并发编程模型:事件驱动 vs 线程

如何理解 Golang 中“不要通过共享内存来通信,而应该通过通信来共享内存”?

flutter入门:线程,异步,声明式UI

声明式UI

UI开发其实经历了纯命令式到半声明式到完全声明式的发展过程。

纯命令式就是UI的构建和修改都是命令式的,目前大多iOS开发都是这类;

半声明式是HTML或安卓的XML这种,UI的构建是依赖于描述语言声明的,但UI的修改仍是命令式的。

而近几年逐渐流行的声明式,但不是XML/HTML这种单独搞出个DSL去描述UI,而是在原本的语言中通过一个数据结构去描述,修改UI时也不是拿到那个View去命令式的修改,而是重新生成这个描述结构。

从命令式迁移到声明式必然有一段适应期,但是你要相信声明式UI确实是比命令式好的。

不谈具体API,其发展过程就是明证:声明式UI从web端的React开始流行,目前web端框架基本上都是声明式的;再扩散到RN/Weex这类跨平台框架,以及小程序框架,更后面的Flutter和SwiftUI也是走了这条路。基本上可以说,未来的UI都会是声明式的了。

看一下代码吧:

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

MyHomePage是个StatefulWidget,有状态的组件,我们关注下build方法中的部分,可以看到build方法返回了一个嵌套的组件结构,UI就是根据这个结构进行构造的,当我们想要刷新UI时,看下面的onPressed,调用了_incrementCounter,触发了setState方法,这个方法在更新数据后会重新触发build,重新生成Widget的声明式树形结构。

阅读 254发布于 2019-11-29

推荐阅读
二师兄的博客
用户专栏

个人博客,希望这次能多写点

3 人关注
41 篇文章
专栏主页
目录