
前言
在Android Studio的学习中,我认识到不同ios、android和PC端之间存在不同。苹果在2008年发布iOS,Google 在2009年发布Android,它们的SDK使用的是不同的编程语言,Objective-C,Swift和Java,Kotlin。开发人员在直接调用平台SDK进行UI开发时,由于语言以及SDK的不同,所以开发人员必须为两个平台分别开发App。那么开发人员的工作量将会大大提升,所以由逐渐推出了很多跨平台的开发。Flutter就是其中的一种。
提示:以下是本篇文章正文内容,下面案例可供参考
一、Flutter是什么?Flutter is Google’s UI toolkit for building beautiful,natively compiled applicatioins for mobile,web,and desktop from a single condebase.(引用自官网)
由上面的介绍可以知道Flutter是Google一个新的用于构建跨平台的手机App的SDK。即写一份代码,在Android 和iOS平台上都可以运行。
越来越多的App包含Flutter,如QQ,QQ邮箱,Soul等的制作中都含有Flutter。这也可以体现出Flutter的实用性。但为什么会选择Flutter呢?
1.使用Dart语言根据大神介绍,Dart语言是构建客户端应用程序的绝佳工具,而且针对UI开发工作做了调整和优化。它与其他语言(C#,Java)相似,所以大部分开发人员可以快速上手并使用它。
代码如下(部分):
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
2.快速开发
使用过Android Studio的朋友都应该被它的速度折服过,而且,虽然Android Studio的xml文件具有布局预览功能,但在自定义View的时候不是每次都像所预期的运行。
但Flutter有“热重载”功能,可以实时看到应用的变化,且不会丢失当前应用程序的状态。“热重载”的实现与Dart语言有关。Dart语言支持JIT(Just In Time)。即能够即时编译或运行时编译。这样就可以实时看到应用的变化了。
## 3.良好的交互体验 用户体验的关键时App的性能,而Flutter不依赖任何中间代码做映射,直接调用底层代码,极大的提高了性能。 而对于开发人员,Flutter可以随时修改屏幕上的任何空间,可以直接使用原生控件来做UI动画,在使用Flutter生成动画的时候,不仅更加灵活和通用,而且不会额外增加工作量。
三、Key的介绍
Flutter积攒了纪念的Widget,像是专门为我们准备的礼物
在介绍Key之前,我们要理解它是谁的参数。
–>Widget
我们要理解Widget到底是什么。
Widget是Flutter应用程序用户界面的基本构建块。
每个Widget都是用户界面一部分的不可变声明。 与其他将视图、控制器、布局和其他属性分离的框架不同,Flutter具有一致的统一对象模型:widget。可以理解为Widget是描述UI元素Element的配置数据。一个Widget可以对应多个Element,即:同一份配置,可生成多个Element;每个Element都会对应一个Widget。
abstract class Widget extends DiagnosticableTree {
const Widget({ this.key });
final Key key;
@protected
Element createElement();
...
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
}
而如何灵活的运用Widget,Key很重要。
Key
此处将Key设置为一级标题,只是为了强调Key,并非格式错误
Widget的构造方法中有一个可选参数Key
abstract class Widget extends DiagnosticableTree {
const Widget({ this.key });
首先我们可以想想:
如果没有Key会发生什么
如果StatelessWidget没有Key不会出现问题,
但StatefulWidget如果没有Key就会导致无法进行添加删除等 *** 作。因为代码无法判断相同的三个代码,就像你无法判断三个长相一直的三胞胎。
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Box(Colors.blue),
Box(Colors.blue),
Box(Colors.blue),
),
],
),
Key就像一个id一样控制Widget。就如同给三胞胎发身份z,我们就可以通过身份z去分辨他们
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Box(Colors.blue,ValueKey(1)),
Box(Colors.blue,ValueKey(2)),
Box(Colors.blue,ValueKey(3)),
),
],
),
所以就可以实现Key控制一个Widget替换另一个Widget。如果两个Widget的runtimeType与Key相同,则表示新的Widget将替换旧的Widget。
但Widget不是最终出现在屏幕上的东西。Element才是;Widget就相当于一个蓝图,而他的实例化就是Element。
所以如果没有key,即两个Widget的runtimeType和Key相同,会update更新Element;否则旧的Element将从树中移出,新的element插入树中。
1.ValueKey
是新建Flutter时候默认的Key,存在与LocalKey中。
是以一个值作为 Key。比如数字,字母等。
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
......
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
例如:
Text("name"),
TextField(key: ValueKey("name"),),
Text("Address"),
TextField(key: ValueKey("Address"),),
2.LocalKey
局部键
除了刚刚在1.中讲解的ValueKey
还存在:
ObjectKey
以一个对象作为 Key。当多个值才能唯一标识的时候,将这多个值组合成一个对象。比如【学校 + 学号】才能唯一标识一个学生。
class ObjectKey extends LocalKey {
/// Creates a key that uses [identical] on [value] for its [operator==].
const ObjectKey(this.value);
/// The object whose identity is used by this key's [operator==].
final Object? value;
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType)
return false;
return other is ObjectKey
&& identical(other.value, value);
}
UniqueKey
生成唯一随机数(对象的 Hash 值)作为 Key。注意:如果直接在控件构建的时候生成,那么每次构建都会生成不同的 Key。
class UniqueKey extends LocalKey {
/// Creates a key that is equal only to itself.
///
/// The key cannot be created with a const constructor because that implies
/// that all instantiated keys would be the same instance and therefore not
/// be unique.
// ignore: prefer_const_constructors_in_immutables , never use const for this class
UniqueKey();
@override
String toString() => '[#${shortHash(this)}]';
}
如果不加上UniqueKey,则动画效果会直接改变,但加上UniqueKey,会逐渐改变文字,由hi逐渐变为hello。
AnimatedSwitcher(
duration:Duration(seconds:1),
child:Text("hello",key:Uniquekey(),),//child:Text("hi",key:Uniquekey(),),
)
3.GlobalKey
全局键
通过 GlobalKey 能够跨 Widget 访问状态。
final _globalKey = GlobalKey();
......
body:Center(
child:Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
counter(),
Center(child:Counter(_globalKey)),
]
)
)
......
class Counter extends StatefulWidget{
Count([Key key]):super(key:key);
@override
_CounterState createState()=>_CounterState();
}
使用Key做一个小程序
提示:这里运用刚刚讲解的Key做一个小游戏:分辨颜色深浅
(此部分为Key的代码)
class Box extends StatelessWidget {
static const width = 60.0;
static const height = 150.0;
static const margin = 2.0;
final Function(Color) onDrag;
final Color color;
final double x,y;
final Function() onEnd;
Box(
this.color,
this.x,
this.y,
this.onDrag,
this.onEnd,
) : super(key: ValueKey(color),);//ValueKey!!!
@override
Widget build(BuildContext context) {
final container = Container(
width: width - margin * 2,
height: height - margin * 2,
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(8.0),//圆边框
),
);
return AnimatedPositioned(//隐式动画,决定拖动时出现躲避的效果
duration: Duration(milliseconds: 100),
top: y,
left: x,
child: Draggable(//Draggable可以实现拖拽
onDragStarted: () => onDrag(color),
onDragEnd: (_) => onEnd(),
child: container,
feedback: container,//拖动时的反馈
childWhenDragging: Container(//留下的渲染
width: width - margin * 2,
height: height - margin * 2,
),
),
);
}
}
而为了可以实现左右拖动时发生位置的变化:
body: Listener(//监听者,监听事件,得到位置
onPointerMove: (event){
final x = event.position.dx;
if (x > (_slot +1)* Box.width) { //实现互换
if (_slot == _colors.length - 1) return;
setState(() {
final c = _colors[_slot];
_colors[_slot] = _colors[_slot + 1];
_colors[_slot + 1] = c;
_slot++;//相应的往后移一位
});
}else if(x < _slot * Box.width){
if (_slot == 0) return;
setState(() {
final c = _colors[_slot];
_colors[_slot] = _colors[_slot - 1];
_colors[_slot - 1] = c;
_slot--;//相应的往前移一位
});
}
},
child: SizedBox(
child: Stack(//自己写类似于ReorderableListView,但更加灵活,不用长按,也可以上下左右的移动
children: List.generate(_colors.length, (i) {
return Box(
_colors[i],
i*Box.width,
200,
(Color color){
final index = _colors.indexOf(color);//得到拖拽的颜色框号码
_slot = index;
},
ValueKey(_colors[i]),
);
}),
),
),
), // This trailing comma makes auto-formatting nicer for build methods.
图片展示:
为了检测所拖动的代码是否成功,加入_checkWinCondition()检测方块
_checkWinCondition(){//检测方块
List<double> lum = _colors.map((c) => c.computerLuminance()).toList();
bool success = true;
for (int i = 0; i < lum.length - 1;i++){
if(lum[i] > lum[i + 1]){
success = false;
break;
}
}
print(success ? "win" : "");
}
图片展示:
成功提示:
为了更加有趣味性,添加颜色多变,点击返回按钮时,颜色能发生随机改变
_shuffle(){
_color = Colors.primaries[Random().nextInt(Colors.primaries.length)];
_colors = List.generate(6, (index) => _color[(index + 1) * 100]);
setState(() => _colors.shuffle());
}
图片展示:
1.8个Flutter的优势以及为什么要在下一个项目中尝试Flutter
2.Flutter 中的 Key
作者:彭馨逸
原文链接
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)