赞
踩
忙里偷闲终于把Flutter中文网大致浏览了一遍,首先感谢Flutter中文网的作者大大对该网站的维护。经过这几个月的学习,对Flutter有了大致的了解,在这段时间对Flutter做下总结和笔记,然后用Flutter写个demo,对就是我以前写的ShowTime,现在有java,kotlin版本,然后就是Flutter版本的了,一开心就想把啥东西都往里集成,哈哈。废话说多了,作为一个Android开发者,我是对照着Android系统知识来学习Flutter的,我把Flutter大致分成了五部分:
Flutter布局机制的核心就是widget。在Flutter中,几乎所有东西都是一个widget --甚至布局模型都是widget。Widgets 是用于构建UI的类.Widgets 用于布局和UI元素.通过简单的widget来构建复杂的widget.flutter工程中的每一个页面都是一个widget树,或者说是一个widget
widget分为有状态的和无状态。
让widget直接继承StatelessWidget就可以了
class CustomStatelessWidget extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return null;
}
}
//这只是做一个直观的列子 //通过继承StatelessWidget(无状态)或StatefulWidget(有状态)来区分无状态或有状态 class AnimatedListSample extends StatefulWidget{ @override State<StatefulWidget> createState() => new _AnimatedListSampleState(); } class _AnimatedListSampleState extends State<AnimatedListSample>{ Widget _itemBuilder(BuildContext context, int index, Animation<double> animation){ return new CardItem(animation: animation, item: _list[index], selected: _selectedItem==_list[index], onTip: (){ //通过setState修改其状态 setState(() { _selectedItem=_selectedItem == _list[index]?null:_list[index]; }); },); } }
自定义State类存储可变信息 - 可以在widget的生命周期内改变逻辑和内部状态, 调用setState()是至关重要的,因为这告诉框架,widget的状态已经改变,应该重绘。
在StatefulWidget调用createState之后,框架将新的状态对象插入树中,然后调用状态对象的initState。 子类化State可以重写initState,以完成仅需要执行一次的工作。 例如,您可以重写initState以配置动画或订阅platform services。initState的实现中需要调用super.initState。
当一个状态对象不再需要时,框架调用状态对象的dispose。 您可以覆盖该dispose方法来执行清理工作。例如,您可以覆盖dispose取消定时器或取消订阅platform services。 dispose典型的实现是直接调用super.dispose。
class LifecycleWatch extends StatefulWidget{ @override State<StatefulWidget> createState() =>LifecycleState(); } class LifecycleState extends State<LifecycleWatch> with WidgetsBindingObserver{ AppLifecycleState _appLifecyclestate; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); } @override void dispose() { super.dispose(); WidgetsBinding.instance.removeObserver(this); } @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); setState(() { _appLifecyclestate=state; }); } Widget showChildWidget(){ if(_appLifecyclestate==null){ return new Text('This widget has not observed any lifecycle changes.', textDirection: TextDirection.ltr); }else{ return new Text('The most recent lifecycle state this widget observed was: $_appLifecyclestate.', textDirection: TextDirection.ltr); } } @override Widget build(BuildContext context) { return showChildWidget; } }
Flutter中的手势系统有两个独立的层。第一层有原始指针(pointer)事件,它描述了屏幕上指针(例如,触摸,鼠标和触控笔)的位置和移动。 第二层有手势,描述由一个或多个指针移动组成的语义动作。
指针(Pointer)代表用户与设备屏幕交互的原始数据。有四种类型的指针事
手势表示可以从多个单独的指针事件(甚至可能是多个单独的指针)识别的语义动作(例如,轻敲,拖动和缩放)。 完整的一个手势可以分派多个事件,对应于手势的生命周期(例如,拖动开始,拖动更新和拖动结束)
GestureDetector:该 widget并不具有显示效果,而是检测由用户做出的手势。可以使用GestureDetector来检测各种输入手势,包括点击、拖动和缩放。许多widget都会使用一个GestureDetector为其他widget提供可选的回调。 例如,IconButton、 RaisedButton、 和FloatingActionButton ,它们都有一个onPressed回调,它会在用户点击该widget时被触发。
在屏幕上的指定位置,可能会有多个手势检测器。所有这些手势检测器在指针事件流经过并尝试识别特定手势时监听指针事件流。 GestureDetector widget决定是哪种手势。
当屏幕上给定指针有多个手势识别器时,框架通过让每个识别器加入一个“手势竞争场”来确定用户想要的手势。“手势竞争场”使用以下规则确定哪个手势胜出
当只有水平(或垂直)拖动识别器时,“手势竞争场”是有益的。在这种情况下,“手势竞争场”将只有一个识别器,并且水平拖动将被立即识别,这意味着水平移动的第一个像素可以被视为拖动,用户不需要等待进一步的手势消歧。
指针按下事件(以及该指针的后续事件)然后被分发到由_命中测试_发现的最内部的widget。 从那里开始,这些事件会冒泡在widget树中向上冒泡,这些事件会从最内部的widget被分发到到widget根的路径上的所有小部件(译者语:这和web中事件冒泡机制相似), 没有机制取消或停止冒泡过程(译者语:这和web不同,web中的时间冒泡是可以停止的)。
Android中的触摸事件分发是从最顶层的view一直检索到最后的子view,子view不处理的话,然后逐级回到顶级view
在Android中,您可以使用Canvas在屏幕上绘制自定义形状。
Flutter有两个类可以帮助绘制画布,CustomPaint和CustomPainter,它们实现算法然后绘制到画布。
class SignaturePainter extends CustomPainter{ SignaturePainter({this.paints}); final List<Offset>paints; @override void paint(Canvas canvas, Size size) { Paint paint = new Paint() ..color=Colors.red ..strokeWidth=3.0 ..strokeCap = StrokeCap.round; for(int i =0;i<paints.length-1;i++){ if(paints[i]!=null&&paints[i+1]!=null){ canvas.drawLine(paints[1], paints[i+1], paint); } } } @override bool shouldRepaint(SignaturePainter oldDelegate) { return oldDelegate.paints!=paints; } } class Signature extends StatefulWidget { SignatureState createState() => new SignatureState(); } class SignatureState extends State<Signature> { List<Offset> _points = <Offset>[]; Widget build(BuildContext context) { return new GestureDetector( onPanUpdate: (DragUpdateDetails details) { setState(() { RenderBox referenceBox = context.findRenderObject(); Offset localPosition = referenceBox.globalToLocal(details.globalPosition); _points = new List.from(_points)..add(localPosition); }); }, onPanEnd: (DragEndDetails details) => _points.add(null), child: new CustomPaint(painter: new SignaturePainter(_points)), ); } }
在Android中,您通常会继承View或已经存在的某个控件,然后覆盖其绘制方法来实现自定义View。
在Flutter中,一个自定义widget通常是通过组合其它widget来实现的,而不是继承。
一个便利的小部件,结合了常见的绘画,定位和大小调整小部件
在重绘期间,首先应用给定的transform,然后绘制decorationz装饰空间,然后绘制子控件,最后显示foregroundDecoration。
在水平方向上排列子widget的列表。 要使子项扩展以填充可用的水平空间,可以将子项包装在Expanded小部件中。只有一个child,可以使用Align或Center来定位child。
Row的布局分六步进行
构造
Row({Key key, MainAxisAlignment mainAxisAlignment: MainAxisAlignment.start, MainAxisSize mainAxisSize: MainAxisSize.max, CrossAxisAlignment crossAxisAlignment: CrossAxisAlignment.center, TextDirection textDirection, VerticalDirection verticalDirection: VerticalDirection.down, TextBaseline textBaseline, List children: const [] })
在垂直方向上排列子widget的列表。要使子项扩展以填充可用的垂直空间,可以将子项包装在Expanded小部件中。只有一个孩子,可以考虑使用Align或Center来定位孩子。 此widget和 Row 属性布局步骤还有构造几乎一样只是主轴和副轴方向互换
一个小部件,它将子节点相对于其边框定位。 如果要以简单的方式重叠多个子项,此类很有用,例如,具有一些文本和图像,用渐变覆盖并且按钮附加到底部。
Stack小部件的每个子节点都已定位或未定位。定位子项是包含在具有至少一个非null属性的定位窗口小部件中的子项。堆栈大小自身包含所有未定位的子项,这些子项根据对齐方式定位 (默认为从左到右环境中的左上角和从右到左环境中的右上角)。然后根据它们的顶部,右侧,底部和左侧属性相对于堆叠放置定位的子项。
构造
Stack({ Key key, AlignmentGeometry alignment:AlignmentDirectional.topStart, TextDirection textDirection, StackFit fit:StackFit.loose, Overflow overflow:Overflow.clip, List < Widget > children:const [] })
属性
Flutter中的页面都是一个一个widget堆叠起来的,虽然其中widget的缩进看着很麻烦,但是由于页面是一个一个widget,可重用性就大大增强,对页面进行适当的文件分级也是必要的,当作一个技能来掌握就可以了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。