都是 vue 玩家,就你不会 flutter?

本系列不包含 Dart 语法笔记,以掌握 Dart 语法为前提记录。不懂可以百度搜一下Dart快速入门教程,如果你本来就懂js,基本可以一两小时内掌握Dart的语法,比如看下Flutter实战上的Dart简介

启动

flutter 入口文件 main.dart

dart 复制代码
import 'package:flutter/material.dart'; // 导入md风格组件

void main() => runApp(MyApp()); // 启动flutter

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) => MaterialApp(home: MyHomePage(title: 'Flutter Demo Home Page'));
}

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

class _MyHomePageState extends State<MyHomePage> {
  Widget build(BuildContext context) => {...};
}

用 vue3 项目来类比,main.dart 相当于 main.ts/js。runApp 相当于 createApp,MyApp 相当传进 createApp 的根组件,MyHomePage 相当于首页组件。

build 相当于 vue 的 h 或者 createVNode。

flutter 的三棵树

flutter 的三棵树分别是 widget、element、renderObject 树。 用 vue 类比就是 widget->template/jsx、element->虚拟 dom、renderObject->浏览器 dom 树。

可能不太准确,但是适合对 flutter 没有概念的人理解 flutter 整个渲染原理。

StatelessWidget

StatelessWidget 非常好理解,就是一个不可变的,没有状态的 widget,用 vue 类比就是一个.vue 文件里面只写个 template 标签,或者再 import 别的组件然后用到 template 上,而且没用 data 函数定义组件的状态。 比如上面的:

dart 复制代码
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) => MaterialApp(home: MyHomePage(title: 'Flutter Demo Home Page'));
}

相当于 vue 的(当然 vue 中不存在可变或者不可变的组件,简答类比理解一下)

js 复制代码
<template>
    <MaterialThemeConfig>
        <MyHomePage :title="Vue Demo Home Page" />
    </MaterialThemeConfig>
</template>
<script setup>
import MaterialThemeConfig from './MaterialThemeConfig.vue' // 给子组件提供Provider,提供全局的MD样式
import MyHomePage from './MyHomePage.vue'
</script>

StatefulWidget

在 flutter 中,Widget 是不可变的,发生变化时需要销毁重建,所以谈不上状态。既然 widget 不可变,那则么实现有状态的 widget 呢,那就是用另一个类帮我们代理生成 widget,每次状态发生改变,那就让代理类生成一个新的 widget。

dart 复制代码
class MyHomePage extends StatefulWidget { // 其实StatefulWidget和StatelessWidget一样不可变
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState(); // 但是Statefule的子widget由State代理生成,也就是Statefule的子widget可以动态切换
}

class _MyHomePageState extends State<MyHomePage> { // State类代理里如果状态发生改变就生成新的widget交给外面的StatefulWidget
  Widget build(BuildContext context) => {...};
}

用 vue 来理解:

ts 复制代码
import { h } from 'vue'
export default {
  setup() {
    // 类似createState
    // do sth
    return {
      // ...
    }
  },
  render: () => h('...'), // 类似build,当主动修改组件的状态时,就重新调用这个render
}

总结:StatefulWidget 不是万金油,要慎用。Widget 是不可变的,更新则意味着销毁 + 重建(build)。StatelessWidget 是静态的,一旦创建则无需更新;而对于 StatefulWidget 来说,在 State 类中调用 setState 方法更新数据,会触发视图的销毁和重建,也将间接地触发其每个子 Widget 的销毁和重建。

所以 StatefulWidget 要用对地方,比如如果根 Widget 是 StatefulWidget 的话,那么他状态一改变整个 widget 树都要重建,可想而知对性能的影响有多严重。

生命周期

对于这种数据驱动的框架,一般都会提供生命周期函数钩子,像 vue 的 onMounted、onUpdated 等等。State 的生命周期可以分为 3 个阶段:创建(插入视图树)、更新(在视图树中存在)、销毁(从视图树中移除)

创建

State 初始化时会依次执行 :构造方法(上面的 createState 方法) -> initState(像 vue 的 create 过程,生命周期只调用一次,用于设置默认值) -> didChangeDependencies -> build,随后完成页面渲染。

更新

Widget 的状态更新,主要由 3 个方法触发:

  • setState:相当于 vue 的 this.xxx = xxx,告诉代理,状态发生了改变,需要去更新视图
  • didChangeDependencies:依赖值发生改变,比如 vue 的全局状态改变,如果组件依赖了全局状态,那么也需要去更新视图
  • didUpdateWidget:比如父 Widget 状态发生改变,会触发子组件的这个方法,还有热更新也是系统会使用这个方法去更新视图。

一旦这三个方法被调用,Flutter 随后就会销毁老 Widget,并调用 build 方法重建 Widget。

销毁

组件被移除,或是页面销毁的时候,系统会调用 deactivate 和 dispose,来移除或销毁组件。相当于 vue 组件的 unmounte 过程,貌似vue组件卸载和销毁是一个周期内执行完,并提供beforeUnmount和unmounted钩子函数。

几种常见过程的生命周期过程

父 widget 状态变化

父 widget:setState -> build 子 widget: didUpdateWidget -> build

下一个 Widget:构建方法 -> (initState) -> didChangeDependencies -> build 当前 Widget: deactivate -> build

上一个 Widget:deactivate(还是调用 deactivate,设计的有点奇怪) -> build 当前 Widget: deactivate -> dispose

App 生命周期

除了 widget 的生命周期,app 也有生命周期,在 widget 中可以通过WidgetsBindingObserver类来调用 App 的生命周期钩子:

dart 复制代码
abstract class WidgetsBindingObserver {
  // 页面 pop
  Future<bool> didPopRoute() => Future<bool>.value(false);
  // 页面 push
  Future<bool> didPushRoute(String route) => Future<bool>.value(false);
  // 系统窗口相关改变回调,如旋转
  void didChangeMetrics() { }
  // 文本缩放系数变化
  void didChangeTextScaleFactor() { }
  // 系统亮度变化
  void didChangePlatformBrightness() { }
  // 本地化语言变化
  void didChangeLocales(List<Locale> locale) { }
  //App 生命周期变化
  void didChangeAppLifecycleState(AppLifecycleState state) { }
  // 内存警告回调
  void didHaveMemoryPressure() { }
  //Accessibility 相关特性回调
  void didChangeAccessibilityFeatures() {}
}

生命周期回调

didChangeAppLifecycleState 回调函数中,有一个参数类型为 AppLifecycleState 的枚举类,这个枚举类是 Flutter 对 App 生命周期状态的封装。它的常用状态包括 resumed、inactive、paused 这三个。

  • resumed:可见的,并能响应用户的输入。
  • inactive:处在不活动状态,无法处理用户响应。
  • paused:不可见并不能响应用户的输入,但是在后台继续活动中。
dart 复制代码
class _MyHomePageState extends State<MyHomePage>  with WidgetsBindingObserver{
  @override
  @mustCallSuper
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);// 注册监听器
  }
  @override
  @mustCallSuper
  void dispose(){
    super.dispose();
    WidgetsBinding.instance.removeObserver(this);// 移除监听器
  }
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) async {
    print("$state");
    if (state == AppLifecycleState.resumed) {
      //do sth
    }
  }
}

以上就是 flutter 的基本知识了,如果会 vue 的话入手 flutter 也是简简单单。如果点赞多就继续写一下布局、组件的快速入门教程XD

相关推荐
TE-茶叶蛋8 小时前
Vue Fragment vs React Fragment
javascript·vue.js·react.js
Angindem8 小时前
从零搭建uniapp项目
前端·vue.js·uni-app
玖夜Kty9 小时前
国内环境修改 flutter.bat 来设置 flutter 的网络环境
flutter
LinXunFeng10 小时前
Flutter - GetX Helper 助你规范应用 tag
flutter·github·visual studio code
前端小白从0开始10 小时前
Vue3项目实现WPS文件预览和内容回填功能
前端·javascript·vue.js·html5·wps·文档回填·文档在线预览
難釋懷11 小时前
Vue解决开发环境 Ajax 跨域问题
前端·vue.js·ajax
挑战者66688812 小时前
vue入门环境搭建及demo运行
前端·javascript·vue.js
程序猿ZhangSir13 小时前
Vue3 项目的基本架构解读
前端·javascript·vue.js
亲亲小宝宝鸭14 小时前
写了两个小需求,终于搞清楚了表格合并
前端·vue.js
Face14 小时前
路由Vue-router 及 异步组件
前端·javascript·vue.js