当前位置: 首页>后端>正文

Flutter 常用方法和基本原理

等待多个异步任务全部结束
1、使用 Future.wait()

Future<void> fetchData() async {
  try {
    var response1 = http.get(Uri.parse('https://example.com/data1'));
    var response2 = http.get(Uri.parse('https://example.com/data2'));
    var results = await Future.wait([response1, response2]);
    var data1 = jsonDecode(results[0].body);
    var data2 = jsonDecode(results[1].body);
    print(data1);
    print(data2);
  } catch (e) {
    print('Error: $e');
  }
}

2、使用 Stream 和 StreamController:Stream 和 StreamController 是 Dart 中用于处理流的类,它们可以用于实现多个异步请求的并发处理和结果组合。我们可以创建多个 StreamController,并将它们的流合并到一个单独的流中,然后使用 Stream 的各种操作符来处理结果。

Future<void> fetchData() async {
  try {
    var controller1 = StreamController();
    var controller2 = StreamController();
    http.get(Uri.parse('https://example.com/data1'))
        .then((response) => controller1.add(response.body));
    http.get(Uri.parse('https://example.com/data2'))
        .then((response) => controller2.add(response.body));
    var results = await StreamZip([controller1.stream, controller2.stream]).toList();
    var data1 = jsonDecode(results[0]);
    var data2 = jsonDecode(results[1]);
    print(data1);
    print(data2);
  } catch (e) {
    print('Error: $e');
  }
}

等待组件渲染完成

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
      showDialog(
          context: context,
          builder: (context) {
            return const AlertDialog(
              title: Text('AlertDialog'),
            );
          });
    });
    print("initState-------");
  }

生命周期阶段执行的函数
1、initState
调用次数:1次

插入渲染树时调用,只调用一次,widget创建执行的第一个方法,这里可以做一些初始化工作,比如初始化State的变量。

2、didChangeDependencies
调用次数:多次

初始化时,在initState()之后立刻调用
依赖的InheritedWidget rebuild,会触发此接口被调用
实测在组件可见状态变化的时候会调用
3、build
调用次数:多次

初始化之后开始绘制界面
setState触发的时候会
4、didUpdateWidget
调用次数:多次

组件状态改变时候调用

5、deactivate
当State对象从树中被移除时,会调用此回调,会在dispose之前调用。

页面销毁的时候会依次执行:deactivate > dispose

6、dispose
调用次数:1次

Flutter 常用方法和基本原理,第1张
生命周期.png

监听app生命周期

 class xxx with WidgetsBindingObserver

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance?.addObserver(this);
}

  @override
  void dispose() {
    WidgetsBinding.instance?.removeObserver(this);
    super.dispose();
  }
  ///监听应用生命周期变化
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);
    print(':didChangeAppLifecycleState:$state');
    switch (state) {
      case AppLifecycleState.inactive: // 处于这种状态的应用程序应该假设它们可能在任何时候暂停。
        break;
      case AppLifecycleState.resumed: //从后台切换前台,界面可见
        //fix Android压后台首页状态栏字体颜色变白,详情页状态栏字体变黑问题
        changeStatusBar();
        break;
      case AppLifecycleState.paused: // 界面不可见,后台
        break;
      case AppLifecycleState.detached: // APP结束时调用
        break;
    }
  }


#### StatelessWidget和StatefulWidget

Flutter中有两种常用的小部件:StatelessWidget和StatefulWidget。它们的主要区别在于它们管理小部件的状态和可变性。

1. **StatelessWidget** :

* 这是一个无状态的小部件,其状态在创建后不会发生变化。
* 它通常用于表示静态内容,如文本标签、图标、按钮等。
* StatelessWidgets对性能要求较低,因为它们不需要跟踪任何状态的变化。
* 当小部件的内容不需要根据用户交互或其他因素而改变时,通常选择StatelessWidget。

class MyTextWidget extends StatelessWidget {
final String text;

MyTextWidget(this.text);

@override
Widget build(BuildContext context) {
return Text(text);
}
}


2. **StatefulWidget** :

* 这是一个有状态的小部件,其状态可以随时间变化。
* 它通常用于需要根据用户输入、网络响应或其他外部因素更新UI的情况。
* StatefulWidget的状态是可变的,可以通过调用 `setState()`方法来通知Flutter重新构建小部件。
* 当小部件的内容需要根据可变数据或用户交互而改变时,通常选择StatefulWidget。

class MyCounterWidget extends StatefulWidget {
@override
_MyCounterWidgetState createState() => _MyCounterWidgetState();
}

class _MyCounterWidgetState extends State<MyCounterWidget> {
int _counter = 0;

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

@override
Widget build(BuildContext context) {
  return Column(
    children: [
      Text('Count: $_counter'),
      ElevatedButton(
        onPressed: _incrementCounter,
        child: Text('Increment'),
      ),
    ],
  );
}

}


选择使用哪个小部件取决于您的需求。如果您的UI内容不会改变,或者仅在初始化时改变,那么StatelessWidget是一个更好的选择。如果您需要在用户交互或数据更新时更新UI,那么StatefulWidget是更合适的选择。通常,Flutter应用程序中会同时使用这两种类型的小部件,以满足不同的需求。

#### setState

setState() 方法内部的工作原理如下:

首先,Flutter 框架会记录需要重建的 Widget。

然后,Flutter 框架会调用 build() 方法来重建 Widget。

在 build() 方法中,Flutter 框架会根据 Widget 的新状态来构建 Widget 树,并返回一个新的 Widget 树。

最后,Flutter **框架会比较新旧** Widget **树的差异,并将差异应用到渲染树中,以更新**显示。

#### **Widget 树、Element 树与 RenderObjc 树**

在Flutter中,有三个主要概念:Widget树、Element树和RenderObject树,它们分别用于构建、管理和渲染UI元素。

1.**Widget树** :

* Widget树是Flutter UI的基础,它是由一系列嵌套的小部件(Widgets)构成的树状结构。
* Widgets是不可变的,它们用来描述UI的外观和布局,以及UI的交互行为。
* Widget树定义了UI的层次结构和布局,但它们不包含实际的渲染信息。
* Widget树是通过调用 `build()`方法构建的,该方法返回一个根据当前状态和数据生成的小部件。

2.**Element树** :

* Element树是Widget树的运行时表示,它负责管理小部件的状态和生命周期。
* 当Widget树需要重新构建时,Flutter会创建新的Element树,与Widget树进行对比,并更新旧的Element以反映新的Widget树。
* Element树中的元素(Elements)可以持有与小部件关联的状态信息,以便在需要时更新UI。
* Element树的主要作用是管理小部件的状态和生命周期,以及将小部件与RenderObject树连接起来。

3.**RenderObject树** :

* RenderObject树是Flutter的渲染引擎的一部分,它负责将Widget树中的小部件渲染成屏幕上的可见内容。
* 每个小部件都对应一个RenderObject,RenderObject负责计算小部件的布局、绘制和渲染。
* RenderObject树是一个高效的树状结构,用于处理小部件的绘制和布局,以便将它们呈现在屏幕上。
* RenderObject树的主要作用是处理小部件的渲染和布局,以将UI呈现到屏幕上。

这三个树状结构在Flutter中协同工作,Widget树描述UI的外观和结构,Element树管理小部件的状态和生命周期,RenderObject树负责将UI渲染到屏幕上。它们之间的协作使Flutter能够高效地构建、更新和渲染UI,同时提供了灵活性和性能。理解这些树的概念对于开发高质量的Flutter应用程序非常重要。

#### widget更新机制

abstract class Widget extends DiagnosticableTree {
/// Initializes [key] for subclasses.
const Widget({ this.key });
final Keykey;

@protected
@factory
Element createElement();

@override
@nonVirtual
bool operator ==(Object other) => super == other;

static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
}


widget类继承自DiagnosticableTree,即“诊断树”,主要作用是提供调试信息。

Key主要的作用是决定是否在下一次build时复用旧的 widget ,决定的条件在canUpdate()方法中。

createElement():一个 widget 可以对应多个Element,Flutter 框架在构建UI树时,会先调用此方法生成对应节点的Element对象。此方法是 Flutter 框架隐式调用的,在开发过程中基本不会调用到。

Element来调用canUpdate()来决定是否需要更新Ui

canUpdate(...)**是一个静态方法,只要**newWidget**与**oldWidget**的**runtimeType**和**key**同时相等,**

**就会用**new widget**去更新旧**UI**树上所对应的**Element**对象的配置,配置相同就不更新UI了,配置不同更新RenderObjc的UI,**

**如果canUpdate返回NO  就会创建新的**Element和**RenderObject,更新UI。**

#### BuildContext

widget都是写在build方法里的,那方法参数BuildContext是什么呢?

Widget build(BuildContext context) {


[BuildContext] 对象实际上是 [Element] 对象。 [BuildContext] 接口用于阻止直接操作 [Element] 对象。

根据官方的注释,我们可以知道 BuildContext 实际上就是 Element 对象,主要是为了防止开发者直接操作 Element 对象。通过源码我们也可以看到 Element 是实现了 BuildContext 这个抽象类
这里以Stateless Widget为例,当要把这个widget装进视图树的时候,首先会去createElement,并将当前widget传给Element。

abstract class StatelessWidget extends Widget {
const StatelessWidget({ Key key }) : super(key: key);
@override
StatelessElement createElement() => StatelessElement(this);
...


我们再来看一看这个StatelessElement是什么,根据下面代码我们可以看到,通过将widget传入StatelessElement的构造函数,StatelessElement保留了widget的引用,并且将会调用build方法。而这个build方法真正调用的则是widget的build方法,并将this(也就是该StatelessElement对象)传入。

class StatelessElement extends ComponentElement {
/// Creates an element that uses the given widget as its configuration.
StatelessElement(StatelessWidget widget) : super(widget);

@override
StatelessWidget get widget => super.widget;

@override
Widget build() => widget.build(this);

@override
void update(StatelessWidget newWidget) {
super.update(newWidget);
assert(widget == newWidget);
_dirty = true;
rebuild();
}
}


**BuildContext对象实际上就是Element对象!**

**但为什么要这样做呢?官方的解释如下:**

大致的意思是用阻止对 Element 对象的直接操作。那么问题又来了,如果对 Element 对象的直接操作会导致什么问题呢?主要原因如下:

[Element]对象是由Flutter框架创建和管理的,它代表了Widget树中的一个具体节点。直接操作[Element]对象可能会破坏Flutter框架的内部逻辑,因此,我们应该避免直接操作[Element]对象。而使用[BuildContext],我们可以通过BuildContext对象获取到需要的信息,而不需要直接操作[Element]对象

那么 BuildContext 到底能干什么呢?只要是 Element 能做的事情,BuildContext 基本都能做,如:通过 context 之前获取到宽高度,距离左上角的偏移,element 对应的 widget 等

var size = (context.findRenderObject() as RenderBox).size;
var local = (context.findRenderObject() as RenderBox).localToGlobal;
var widget = context.widget;


因为 Elment 是继承自 BuildContext ,我们甚至可以通过 context 来直接刷新 Element 的状态,这样就可以直接对当前的 Element 进行刷新,而不必去通过 SetState,但是这种做法是不推荐的

(context as Element).markNeedsBuild();


其实在 setState 中,最终也是调用的 `markNeedsBuild` 方法,

void setState(VoidCallback fn) {
最终调用
_element!.markNeedsBuild();
}


https://www.xamrdz.com/backend/3sw1934073.html

相关文章: