Flutter:use_key_in_widget_constr
一.Flutter的构造器
BAD:
class MyPublicWidget extends StatelessWidget {
}
GOOD:
class MyPublicWidget extends StatelessWidget {
const MyPublicWidget({Key? key}) : super(key: key);
}
(1)为什么使用const
这里引入一下 final
或 const
的区别
-
编译时就固定变量的值,可以使用
const
类型变量 -
运行时再被赋值且只能被赋值一次,可以使用
final
类型变量 -
const
关键字不仅可以用于声明常量变量。 还可以用来创建常量值,以及声明创建常量值的构造函数
什么是创建常量值的构造函数
- 常量构造函数需以const关键字修饰
- const构造函数必须用于成员变量都是final的类
- 构建常量实例必须使用定义的常量构造函数
- 如果实例化时不加const修饰符,即使调用的是常量构造函数,实例化的对象也不是常量实例
class ImmutablePoint {
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
final num x, y;
const ImmutablePoint(this.x, this.y);
}
常量构造函数生成的一定是常量对象吗?
答案是否定的
-
使用
常量构造函数
,在构造函数名之前加const
关键字,来创建编译时常量var p = const ImmutablePoint(2, 2);
构造两个相同的编译时常量会产生一个唯一的, 标准的实例:
var a = const ImmutablePoint(1, 1); var b = const ImmutablePoint(1, 1); assert(identical(a, b)); // 它们是同一个实例。
-
使用
常量构造函数
, 且省略了const
关键字, 此时创建的对象是非常量对象var a = const ImmutablePoint(1, 1); // 创建一个常量对象 var b = ImmutablePoint(1, 1); // 创建一个非常量对象 assert(!identical(a, b)); // 两者不是同一个实例!
(2)构造器参数{}
是什么?
首先`Dart`分为以下几种
-
位置参数
- 必须参数 required
- 位置可选参数 optional(
将参数放到
[ ]中来标记参数是可选的
)
String say(String from, String msg, [String device]) { var result = '$from says $msg'; if (device != null) { result = '$result with a $device'; } return result; }
上述代码
from
,msg
属于必须参数
[String device]
属于位置可选参数下面是不使用可选参数调用上面方法 的示例:
assert(say('Bob', 'Howdy') == 'Bob says Howdy');
下面是使用可选参数调用上面方法的示例:
```dart
assert(say('Bob', 'Howdy', 'smoke signal') ==
'Bob says Howdy with a smoke signal');
```
> 注意 可选位置参数一定要要必须位置参数后面
-
命名参数
-
命名可选参数 optional (使用
{param1, param2, …}
来指定命名参数)定义函数:
/// Sets the [bold] and [hidden] flags ... void enableFlags({bool bold, bool hidden}) {...}
调用函数:
enableFlags(bold: true, hidden: false);
-
注意 可选位置参数一定要要必须位置参数后面
使用 @required
注释表示参数是 required 性质的命名参数:
const Scrollbar({Key key, @required Widget child})
此时 Scrollbar
是一个构造函数, 当 child
参数缺少时,分析器会提示错误
(3)Key
是什么?
key
我们可以理解为每个控件所独有的一个ID,用以识别是哪个UI。Key 能够帮助开发者在 Widget tree 中保存状态,有了key
,element
和renderObject
就会被强行重建,从而得到所见即所得的效果
key
的分类
-
LocalKey (局部key)
-
ValueKey :可以传入任何对象,比较的时候会对比它的内容是否一致
@override bool operator ==(Object other) { if (other.runtimeType != runtimeType) return false; return other is ValueKey<T> && other.value == value; }
-
-
ObjectKey :可以传入任何参数,比较的时候会比较它的指针是否一致,即是否为同一个对象
@override bool operator ==(Object other) { if (other.runtimeType != runtimeType) return false; return other is ObjectKey && identical(other.value, value); }
-
UniqueKey : 不需要传任何参数,每次刷新都会生成一个新的值,通常用于动画的过渡当中
AnimatedSwitcher( duration: const Duration(seconds: 1), child: Text("no keyrrths", key: UniqueKey()), )
每次改变文字时,假如不传uniqueKey,就不会有动画的渐变效果,而如果传了UniqueKey,则会有渐变动画效果。因为不传uniqueKey时,每次都只会认为text的widget发生了变化,只会将text的widget给替换为新的widget,而element还是同一个不会变化,所以会认为UI没有发生变化,因此不会改变;而如果传了uniqueKey时,每次widget比较时都会因为自身的key不一致而被认为是不同的widget,导致会重建element和renderObject,前后两个UI不一致,此时就会发生动画效果。
-
GlobalKey (全局key)
全局变量,顾名思义,整个应用程序里都是唯一的,所以同一个
GlobalKey
只能作用在一个widget上。可以通GlobalKey
过拿到所对应的state
和element
或者widget
,用以改变state的状态或者变量值,以及刷新UI。不过不推荐这样做,一般推荐通过控制外面的变量来刷新UIfloatingActionButton: FloatingActionButton( onPressed: (){ var state = (_globalKey as BoxState); state.count++; state.setState(() { }); }, child: const Icon(Icons.add), ),
(注意,这里带下划线的变量为私有变量,文件外部引用不到)
(4).use_key_in_widget_constructors 两种解决办法
1.采用推荐的构造器
class MyPublicWidget extends StatelessWidget {
const MyPublicWidget({Key? key}) : super(key: key);
}
2.在项目的根目录analysis_options.yaml
文件添加忽略
# Add these lines
linter:
rules:
use_key_in_widget_constructors: false