Flutter从入门到实战
一共分为23个系列
①(Flutter、Dart环境搭建篇) 共3个内容 已更新
②(Dart语法1 篇) 共4个内容 已更新
③(Dart语法2 篇) 共2个内容 已更新
④(Flutter案例开发篇) 共4个内容 已更新
⑤(Flutter的StatelessWidget 共3个内容 已更新
⑥(Flutter的基础Widget篇) 共2个内容 已更新
⑦(布局Widget篇) 共1个内容 已更新
⑧(Flex、Row、Column以及Flexible、Stack篇) 共1个内容 已更新
⑨(滚动的Widget篇) 共4个内容 已更新
⑩(Dart的Future和网络篇) 共3个内容 已更新
⑪(豆瓣案例-1篇) 共3个内容 已更新
⑫(豆瓣案例-2篇) 共3个内容 已更新
⑬(Flutter渲染流程篇) 共3个内容 已更新
官方文档说明
官方视频教程
Flutter的YouTube视频教程-小部件
⑬、Flutter渲染流程篇 📚小贴士小贴士1. dart的打印log封装 打印所在文件以及所在行log.dart 小贴士1. dart打印log的使用 ①、三棵树-Widget-Element-RenderObject的关系1. Widget是什么2. Element是什么3. RenderObject是什么4.总结 ②、widget的key的使用1. 未绑定key 引发的问题如何防止颜色发生改变这个问题出现正确流程 但是不是我们想要的效果 2. 绑定key的作用3. Key的分类3.1 LocalKey3.2 GlobalKey3.2 案例代码-GlobalKey 📚小贴士 小贴士1. dart的打印log封装 打印所在文件以及所在行 log.dart
void yhLog(Object message, StackTrace current) {
YHCustomTrace programInfo = YHCustomTrace(current);
print("所在文件: ${programInfo.fileName}, 所在行: ${programInfo.lineNumber}, 打印信息: $message");
}
class YHCustomTrace {
final StackTrace? _trace;
String fileName = "";
int lineNumber = 0;
int columnNumber = 0;
YHCustomTrace(this._trace) {
_parseTrace();
}
void _parseTrace() {
var traceString = this._trace.toString().split("\n")[0];
var indexOfFileName = traceString.indexOf(RegExp(r'[A-Za-z_]+.dart'));
var fileInfo = traceString.substring(indexOfFileName);
var listOfInfos = fileInfo.split(":");
this.fileName = listOfInfos[0];
this.lineNumber = int.parse(listOfInfos[1]);
var columnStr = listOfInfos[2];
columnStr = columnStr.replaceFirst(")", "");
this.columnNumber = int.parse(columnStr);
}
}
小贴士1. dart打印log的使用
yhLog("首页网络请求测试 -- ${res}", StackTrace.current);
log的打印信息效果
①、三棵树-Widget-Element-RenderObject的关系
Flutter 渲染都需要经过
layout
和paint
过程
Widget
是没有经过layout
和paint
过程
那么Widget
最终是通过RenderObject
去渲染出来的
Widget-Element-RenderObject关系
相当于
HTML 转换成 虚拟DOM 再转换成 真实DOM
HTML > 虚拟DOM > 真实DOM
虚拟 DOM
到底是什么,说简单点,就是一个普通的 JavaScript 对象,包含了 tag、props、children 三个属性。
具体了解虚拟DOM可以了解这篇文章 https://juejin.cn/post/6844903870229905422
HTML
hello world!!!
上面的 HTML 转换为虚拟 DOM 如下:
{ tag: 'div', props: { id: 'app' }, chidren: [ { tag: 'p', props: { className: 'text' }, chidren: [ 'hello world!!!' ] } ] }
我们继续回到讲解 Widget-Element-RenderObject 关系
Widget-Element-RenderObject每个都有其对应的key
拿到Widget的key和Element的key 进行一个对比 判断它们的类型和状态是否相同
如果相同 就不需要更新对应key
的RenderObject
用最少的开销
更新 对应的RenderObject
,你想具体更加深入key和三者之间的关系可以查看下面的文档或者视频讲解
查看key的具体文档和视频讲解
key的官方文档
key的官方教程视频讲解
👇 如果上面访问不了那么你可以通过我B站👇的视频查看
When to Use Keys - Flutter Widgets 101 Ep. 4
1. Widget是什么2. Element是什么Widget官网文档说明
Flutter 小部件是使用从React中汲取灵感的现代框架构建的。中心思想是你用小部件构建你的 UI。小部件描述了给定当前配置和状态的视图应该是什么样子。当小部件的状态发生变化时,小部件会重建其描述,框架会根据之前的描述进行比较,以确定底层渲染树从一种状态转换到下一种状态所需的最小更改。
3. RenderObject是什么Element是什么
一个抽象类,所有 HTML 元素都对其进行扩展。
4.总结RenderObject是什么
渲染树中的一个对象。
RenderObject类层次结构是渲染库存在的核心。
RenderObject有一个parent,并且有一个称为parentData的槽,父RenderObject可以在其中存储特定于子的数据,例如子位置。RenderObject类还实现了基本的布局和绘制协议。
然而, RenderObject类没有定义子模型(例如,一个节点是否有零个、一个或多个子节点)。它也没有定义坐标系(例如,孩子是否位于笛卡尔坐标、极坐标等中)或特定的布局协议(例如,布局是宽度进高还是大小限制-out,或者父级是否在子级布局之前或之后设置子级的大小和位置等;或者实际上是否允许子级读取其父级的parentData插槽)。
RenderBox子类引入了布局系统使用笛卡尔坐标的观点。
我们可以到系统查看 一些 Widget
Container、Text、自己封装的widget 这些Widget都不会生成RenderObject
比如
虽然Widget 不会创建RenderObject, 但是Widget的类有一个抽象方法是
createElement
Padding、Row 这些Widget 都是属于渲染Widget 继承链最终继承于RenderObject 或者是生成 RenderObject
结论:
一、不是渲染的RenderWidget
1.所有的Widget都会创建Element
有些是StateLessElement 有些是StateFulElement
2. 渲染的Widget都会创建 RenderObjectElement
3. ComponentElement最终调用mount方法
4. mount 调用了 _firstBuild(); 方法
5. 调用了 rebuild() 调用了 performRebuild 调用了 Widget的build
6. 所有的Widget创建的时候 都会调用 build方法
二、渲染的ReaderWidget
通过ReanderObjectElement : mount -> _widget.createRanderObject
不是渲染的RenderWidget 和 渲染的ReaderWidget 区别是创建的方式不一样
三、Element 有两个属性 一个是 widget 、一个是_readerObject
四、StateLessElement 和 StateFulElement
StateFulElement
构造方法中 _state = widget.createState()
_state.widget = widget
这就是我们为什么可以在State里面通过this.widget获取我们上层的widget
五、简单总结
widget通过底层去创建一个Element
Element通过mount方法
如果 不是渲染的RenderElement 回到Widget去调用build方法
如果 是渲染的RenderElement 去调用createRanderObject方法
上面还会根据Element 是不是StateFulElement
如果是 StateFulElement 还会去widget.createState()
widget通过build 都会有一个回调 BuildContext content
BuildContext content 实际上就是 Element
通过Element知道是哪个widget 。可以拿到它 往上查找信息
②、widget的key的使用
查看key的具体文档和视频讲解
通过一个案例来讲解widget的key
一个页面有3个Widget 背景颜色是随机的。还一个悬浮按钮。通过点击按钮删除最顶层的Widget 并且对应的Widget 颜色是不会变的
下面的代码是通过点击之后 删除第一个数据
import 'dart:math';
import 'package:flutter/material.dart'; // runApp在这个material库里面
main() => runApp(MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: YHiOSHomePage(),
);
}
}
class YHiOSHomePage extends StatefulWidget {
@override
State<YHiOSHomePage> createState() => _YHiOSHomePageState();
}
class _YHiOSHomePageState extends State<YHiOSHomePage> {
final List<String> names = ["aaa","bbb","ccc"];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("列表测试"),
),
body: ListView(
children: names.map((item){
return ListItemLess(item);
}).toList(),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.delete),
onPressed: (){
// setState 会重新build
setState(() {
names.removeAt(0);
});
},
),
);
}
}
class ListItemLess extends StatelessWidget {
final String name;
final Color randColor = Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256));
ListItemLess(this.name);
@override
Widget build(BuildContext context) {
return Container(
child: Text(name),
height: 90,
color: randColor,
);
}
}
通过上面的Gif动画我们发现
我们每删除一个 发现Widget的背景颜色都发生变化。
这个是 SetSate每次都会进行一个build *** 作
我们尝试一下将 StatelessWidget 改成 StatefulWidget
我们发现神奇的事情就是 我们删除的是第一个。但是看到最顶部的颜色不变。删掉了最底部的
这个其实是复用问题。
其实就是我们的Widget 删除了第一个
Element并且是StateFulElement的
Element针对Widget进行了创建。并且每个Element都包含了state
然后通过Widget删除第一个。
Element之前因为存在了3个state。然后参看Widget当前有多少Widget
当前Widget存在2个。所以Element保留前面的 删掉最后一个
ListItemFul(this.name ,{Key? key}): super(key:key); // 绑定
ListItemFul(item,key: ValueKey(item),);// 使用
import 'dart:math';
import 'package:flutter/material.dart'; // runApp在这个material库里面
main() => runApp(MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: YHiOSHomePage(),
);
}
}
class YHiOSHomePage extends StatefulWidget {
@override
State<YHiOSHomePage> createState() => _YHiOSHomePageState();
}
class _YHiOSHomePageState extends State<YHiOSHomePage> {
final List<String> names = ["aaa","bbb","ccc"];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("列表测试"),
),
body: ListView(
children: names.map((item){
// (需求很少)如果删除 向重新构建ListItemFul 可以给key绑定一个随机数 但是随机数还是有可能是相同的。所以可以使用UniqueKey()
// 数据names是唯一的,不用害怕被改变。
// return ListItemFul(item,key: ValueKey(item),); // ⭐️常用
// return ListItemFul(item,key: ValueKey(Random().nextInt(10000)),);
return ListItemFul(item,key: UniqueKey(),);
}).toList(),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.delete),
onPressed: (){
// setState 会重新build
setState(() {
names.removeAt(0);
});
},
),
);
}
}
class ListItemLess extends StatelessWidget {
final String name;
final Color randColor = Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256));
ListItemLess(this.name);
@override
Widget build(BuildContext context) {
return Container(
child: Text(name),
height: 90,
color: randColor,
);
}
}
// 使用StateFul的Widget
class ListItemFul extends StatefulWidget {
final String name;
// 绑定key
ListItemFul(this.name ,{Key? key}): super(key:key);
@override
State<ListItemFul> createState() => _ListItemLessState();
}
class _ListItemLessState extends State<ListItemFul> {
final Color randColor = Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256));
@override
Widget build(BuildContext context) {
return Container(
child: Text(widget.name),
height: 90,
color: randColor,
);
}
}
3. Key的分类
3.1 LocalKey
3.2 GlobalKey
ValueKey
-泛型key、ObjectKey
-对象key、UniqueKey
- 唯一的key
3.2 案例代码-GlobalKey全局的key 作用是 :
GlobalKey
能访问某个Widget的信息
案例 : 向通过悬浮按钮点击 拿到HomePage和HomeContent的值
1.可以是用静态变量 但是静态变量 是类的共享属性的
我们需要的是对象属性
2. 使用GlobalKey
import 'dart:math';
import 'package:flutter/material.dart'; // runApp在这个material库里面
main() => runApp(MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: YHHomePage(),
);
}
}
class YHHomePage extends StatelessWidget {
// 声明一个GlobalKey
// ⭐️GlobalKey 能访问某个Widget的信息
final GlobalKey<_YHHomeContentState> homeKey = GlobalKey();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("列表测试"),
),
body:YHHomeContent(key:homeKey),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.gesture),
onPressed: (){
print(homeKey.currentState?.message);
print(homeKey.currentState?.widget.name);
},
),
);
}
}
class YHHomeContent extends StatefulWidget {
final String name = "宇夜iOS";
YHHomeContent({Key? key}) : super(key: key);
@override
State<YHHomeContent> createState() => _YHHomeContentState();
}
class _YHHomeContentState extends State<YHHomeContent> {
final String message = "123";
@override
Widget build(BuildContext context) {
return Text(message);
}
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)