flutter之,flutter百度百科

九、Flutter之webview

Flutter的webview常用的第三方库有 flutter_webview_plugin 、 webview_flutter ,后者的文档较少,暂先学习flutter_webview_plugin。

10余年建站经验, 成都做网站、网站设计客户的见证与正确选择。创新互联建站提供完善的营销型网页建站明细报价表。后期开发更加便捷高效,我们致力于追求更美、更快、更规范。

添加依赖

导入包

iOS端info.plist配置,其中NSAppTransportSecurity节点是为了支持http协议

一个简单的demo

要监听链接跳转的话,实现onUrlChanged即可

添加依赖

导入包

iOS端info.plist配置

一个简单的demo

但是在webview里点击链接跳转的时候,测试机有时会跳转到系统浏览器上,并且点击文本框无法弹出键盘,交互性很弱。

二者共同的缺点是与javascript难以交互,目前只能实现Flutter-JS传递信息,还没找到可以进行完美交互的第三方库。并且一些常见的协议还不支持,比如拨号和调用摄像头等,期待后续完善。

Flutter 之 文件操作(二十九)

Dart的 IO 库包含了文件读写的相关类,它属于 Dart 语法标准的一部分,所以通过 Dart IO 库,无论是 Dart VM 下的脚本还是 Flutter,都是通过 Dart IO 库来操作文件的,不过和 Dart VM 相比,Flutter 有一个重要差异是文件系统路径不同,这是因为Dart VM 是运行在 PC 或服务器操作系统下,而 Flutter 是运行在移动操作系统中,他们的文件系统会有一些差异。

Android 和 iOS 的应用存储目录不同, PathProvider 插件提供了一种平台透明的方式来访问设备文件系统上的常用位置。该类当前支持访问两个文件系统位置:

File代表一个整体的文件,他有三个构造函数,分别是:

文件读取本身有两种形式,一种是文本,一种是二进制。

2.2.1 读取文本内容

如果是文本文件,File提供了readAsString、readAsLines、readAsStringSync、readAsLinesSync方法,读取文本内容

readAsString 一次性读取所有文本

readAsLines 一行行的读取文本

结果返回的是一个List,list中表示文件每行的内容

readAsStringSync、readAsLinesSync同步读取文本

2.2.2 读取二进制内容

如果文件是二进制,那么可以使用readAsBytes或者同步的方法readAsBytesSync:

dart中表示二进制有一个专门的类型叫做Uint8List,他实际上表示的是一个int的List。

上面提到的读取方式,都是一次性读取整个文件,缺点就是如果文件太大的话,可能造成内存空间的压力。

所以File为我们提供了另外一种读取文件的方法,流的形式来读取文件.

示例

dart提供了open和openSync两个方法来进行随机文件读写:

写入和文件读取一样,可以一次性写入或者获得一个写入句柄,然后再写入。

一次性写入的方法有四种,分别对应字符串和二进制

句柄形式可以调用openWrite方法,返回一个IOSink对象,然后通过这个对象进行写入:

默认情况下写入是会覆盖整个文件的,但是可以通过下面的方式来更改写入模式:

虽然dart中所有的异常都是运行时异常,但是和java一样,要想手动处理文件读写中的异常,则可以使用try,catch:

我们还是以计数器为例,实现在应用退出重启后可以恢复点击次数。 这里,我们使用文件来保存数据:

1.引入PathProvider插件;在pubspec.yaml文件中添加如下声明:

执行 flutter pub get

2.实现如下

参考:

Flutter 之 可滚动组件 -- 理论知识点(十四)

Flutter 中有两种布局模型:

基于 RenderBox 的盒模型布局。

基于 Sliver ( RenderSliver ) 按需加载列表布局。

通常可滚动组件的子组件可能会非常多、占用的总高度也会非常大;如果要一次性将子组件全部构建出将会非常昂贵!为此,Flutter中提出一个Sliver(中文为“薄片”的意思)概念,Sliver 可以包含一个或多个子组件。Sliver 的主要作用是配合:加载子组件并确定每一个子组件的布局和绘制信息,如果 Sliver 可以包含多个子组件时,通常会实现按需加载模型。

只有当 Sliver 出现在视口中时才会去构建它,这种模型也称为“基于Sliver的列表按需加载模型”。可滚动组件中有很多都支持基于Sliver的按需加载模型,如 ListView 、 GridView ,但是也有不支持该模型的,如 SingleChildScrollView 。

Flutter 中的可滚动主要由三个角色组成: Scrollable 、 Viewport 和 Sliver :

具体布局过程:

比如有一个 ListView,大小撑满屏幕,假设它有 100 个列表项(都是RenderBox)且每个列表项高度相同,结构如图6-1所示:

图中白色区域为设备屏幕,也是 Scrollable 、 Viewport 和 Sliver 所占用的空间,三者所占用的空间重合,父子关系为:Sliver 父组件为 Viewport,Viewport的 父组件为 Scrollable 。注意ListView 中只有一个 Sliver,在 Sliver 中实现了子组件的按需加载。

其中顶部和底部灰色的区域为 cacheExtent,它表示预渲染的高度,需要注意这是在可视区域之外,如果 RenderBox 进入这个区域内,即使它还未显示在屏幕上,也是要先进行构建的,预渲染是为了后面进入 Viewport 的时候更丝滑。cacheExtent 的默认值是 250,在构建可滚动列表时我们可以指定这个值,这个值最终会传给 Viewport。

用于处理滑动手势,确定滑动偏移,滑动偏移变化时构建 Viewport,我们看一下其关键的属性:

在可滚动组件的坐标描述中,通常将滚动方向称为主轴,非滚动方向称为纵轴。由于可滚动组件的默认方向一般都是沿垂直方向,所以默认情况下主轴就是指垂直方向,水平方向同理。

Viewport 比较简单,用于渲染当前视口中需要显示 Sliver。

需要注意的是:

Sliver 主要作用是对子组件进行构建和布局,比如 ListView 的 Sliver 需要实现子组件(列表项)按需加载功能,只有当列表项进入预渲染区域时才会去对它进行构建和布局、渲染。

Sliver 对应的渲染对象类型是 RenderSliver,RenderSliver 和 RenderBox 的相同点是都继承自 RenderObject 类,不同点是在布局的时候约束信息不同。RenderBox 在布局时父组件传递给它的约束信息对应的是 BoxConstraints ,只包含最大宽高的约束;而 RenderSliver 在布局时父组件(列表)传递给它的约束是对应的是 SliverConstraints 。关于 Sliver 的布局协议,我们将在本章最后一节中介绍。

几乎所有的可滚动组件在构造时都能指定 scrollDirection (滑动的主轴)、 reverse (滑动方向是否反向)、 controller 、 physics 、 cacheExtent ,这些属性最终会透传给对应的 Scrollable 和 Viewport,这些属性我们可以认为是可滚动组件的通用属性,后续再介绍具体的可滚动组件时将不再赘述。

可滚动组件都有一个 controller 属性,通过该属性我们可以指定一个 ScrollController 来控制可滚动组件的滚动,比如可以通过ScrollController来同步多个组件的滑动联动。由于 ScrollController 是需要结合可滚动组件一起工作,所以本章中,我们会在介绍完 ListView 后详细介绍 ScrollController。

Scrollbar是一个Material风格的滚动指示器(滚动条),如果要给可滚动组件添加滚动条,只需将Scrollbar作为可滚动组件的任意一个父级组件即可,如:

Scrollbar 和 CupertinoScrollbar 都是通过监听滚动通知来确定滚动条位置的。关于的滚动通知的详细内容我们将在本章最后一节中专门介绍。

CupertinoScrollbar是 iOS 风格的滚动条,如果你使用的是Scrollbar,那么在iOS平台它会自动切换为CupertinoScrollbar

Flutter 之 交互

手势操作在 Flutter 中分为两类:

第一类是原始的指针事件(Pointer Event),即原生开发中常见的触摸事件,表示屏幕上触摸(或鼠标、手写笔)行为触发的位移行为;

第二类则是手势识别(Gesture Detector),表示多个原始指针事件的组合操作,如点击、双击、长按等,是指针事件的语义化封装。

指针事件表示用户交互的原始触摸数据,如手指接触屏幕 PointerDownEvent、手指在屏幕上移动 PointerMoveEvent、手指抬起 PointerUpEvent,以及触摸取消 PointerCancelEvent。在手指接触屏幕,触摸事件发起时,Flutter 会确定手指与屏幕发生接触的位置上究竟有哪些组件,并将触摸事件交给最内层的组件去响应。事件会从这个最内层的组件开始,沿着组件树向根节点向上冒泡分发。通过 hitTestBehavior 去调整组件在命中测试期内应该如何表现,比如把触摸事件交给子组件,或者交给其视图层级之下的组件去响应。关于组件层面的原始指针事件的监听,Flutter 提供了 Listener Widget,可以监听其子 Widget 的原始指针事件。

Listener(

child: Container(

color: Colors.black,

width: 300,

height: 300,

),

onPointerDown: (event) = print("down $event"),// 手势按下回调

onPointerMove:  (event) = print("move $event"),// 手势移动回调

onPointerUp:  (event) = print("up $event"),// 手势抬起回调

);

Gesture 是手势语义的抽象,而如果我们想从组件层监听手势,则需要使用 GestureDetector 。GestureDetector 是一个处理各种高级用户触摸行为的 Widget,与 Listener 一样,也是一个功能性组件。

GestureDetector(// 手势识别

    child: Container(color: Colors.red,width: 50,height: 50),// 红色子视图

    onTap: ()=print("Tap"),// 点击回调

    onDoubleTap: ()=print("Double Tap"),// 双击回调

    onLongPress: ()=print("Long Press"),// 长按回调

    onPanUpdate: (e) {// 拖动回调

      setState(() {

        // 更新位置

        _left += e.delta.dx;

        _top += e.delta.dy;

      });

    },

  ),

Flutter 之 WebSockets (三十一)

Http协议是无状态的,只能由客户端主动发起,服务端再被动响应,服务端无法向客户端主动推送内容,并且一旦服务器响应结束,链接就会断开所以无法进行实时通信。WebSocket协议正是为解决客户端与服务端实时通信而产生的技术,现在已经被主流浏览器支持。目前 Flutter也提供了专门的包来支持WebSocket协议。

web_socket_channel package 提供了我们需要连接到WebSocket服务器的工具。该package提供了一个 WebSocketChannel 允许我们既可以监听来自服务器的消息,又可以将消息发送到服务器的方法。

执行flutter pub get 命令,即可

1. 连接到WebSocket服务器

2. 监听来自服务器的消息

使用一个 StreamBuilder 来监听新消息, 并用一个Text来显示它们

工作原理

WebSocketChannel 提供了一个来自服务器的消息 Stream 。该 Stream 类是 dart:async 包中的一个基础类。它提供了一种方法来监听来自数据源的异步事件。与 Future 返回单个异步响应不同, Stream 类可以随着时间推移传递很多事件。该 StreamBuilder 组件将连接到一个 Stream , 并在每次收到消息时通知Flutter重新构建界面

3. 将数据发送到服务器

为了将数据发送到服务器,我们会add消息给WebSocketChannel提供的sink。

WebSocketChannel 提供了一个 StreamSink ,它将消息发给服务器

StreamSink 类提供了给数据源同步或异步添加事件的一般方法

4. 关闭WebSocket连接

思考:

假如我们想通过WebSocket传输二进制数据应该怎么做(比如要从服务器接收一张图片)?我们发现StreamBuilder和Stream都没有指定接收类型的参数,并且在创建WebSocket链接时也没有相应的配置,貌似没有什么办法……其实很简单,要接收二进制数据仍然使用StreamBuilder,因为WebSocket中所有发送的数据使用帧的形式发送,而帧是有固定格式,每一个帧的数据类型都可以通过Opcode字段指定,它可以指定当前帧是文本类型还是二进制类型(还有其它类型),所以客户端在收到帧时就已经知道了其数据类型,所以flutter完全可以在收到数据后解析出正确的类型,所以就无需开发者去关心,当服务器传输的数据是指定为二进制时,StreamBuilder的snapshot.data的类型就是Listint,是文本时,则为String。

Flutter 之 动画1

对动画系统而言,为了实现动画,它需要做三件事儿:1.确定画面变化的规律;2.根据这个规律,设定动画周期,启动动画;3.定期获取当前动画的值,不断地微调、重绘画面。

这三件事情对应到 Flutter 中,就是 Animation、AnimationController 与 Listener:

1.Animation 是 Flutter 动画库中的核心类,会根据预定规则,在单位时间内持续输出动画的当前状态。Animation 知道当前动画的状态(比如,动画是否开始、停止、前进或者后退,以及动画的当前值),但却不知道这些状态究竟应用在哪个组件对象上。换句话说,Animation 仅仅是用来提供动画数据,而不负责动画的渲染。

2.AnimationController 用于管理 Animation,可以用来设置动画的时长、启动动画、暂停动画、反转动画等。

3.Listener 是 Animation 的回调函数,用来监听动画的进度变化,我们需要在这个回调函数中,根据动画的当前值重新渲染组件,实现动画的渲染。

class NormalAnimateWidget extends StatefulWidget {

@override

StatecreateState()=_NormalAnimateState();

}

class _NormalAnimateState extends Statewith SingleTickerProviderStateMixin{

AnimationController?controller;

Animation?animation;

@override

void initState() {

// TODO: implement initState

super.initState();

/*

* AnimationController

AnimationController用于控制动画,它包含动画的启动forward()、停止stop() 、反向播放 reverse()等方法。

* AnimationController会在动画的每一帧,就会生成一个新的值。

* 默认情况下,AnimationController在给定的时间段内线性的生成从 0.0 到1.0(默认区间)的数字。

* */

/*Ticker

当创建一个AnimationController时,需要传递一个vsync参数,

它接收一个TickerProvider类型的对象,它的主要职责是创建Ticker,定义如下:

abstract class TickerProvider {

//通过一个回调创建一个Ticker

Ticker createTicker(TickerCallback onTick);

}

Flutter 应用在启动时都会绑定一个SchedulerBinding,

通过SchedulerBinding可以给每一次屏幕刷新添加回调,

而Ticker就是通过SchedulerBinding来添加屏幕刷新回调,这样一来,

每次屏幕刷新都会调用TickerCallback。

使用Ticker(而不是Timer)来驱动动画会防止屏幕外动画(动画的UI不在当前屏幕时,如锁屏时)

消耗不必要的资源,因为Flutter中屏幕刷新时会通知到绑定的SchedulerBinding,

而Ticker是受SchedulerBinding驱动的,

由于锁屏后屏幕会停止刷新,所以Ticker就不会再触发。

*/

// 创建动画周期为1秒的AnimationController对象

controller =AnimationController(

vsync:this, duration:const Duration(milliseconds:3000));

/*

* Curve

* 动画过程可以是匀速的、匀加速的或者先加速后减速等。

* Flutter中通过Curve(曲线)来描述动画过程,

* 我们把匀速动画称为线性的(Curves.linear),而非匀速动画称为非线性的。

* 我们可以通过CurvedAnimation来指定动画的曲线,如:

final CurvedAnimation curve =

CurvedAnimation(parent: controller, curve: Curves.easeIn);

*

Curves曲线 动画过程

linear 匀速的

decelerate 匀减速

ease 开始加速,后面减速

easeIn 开始慢,后面快

easeOut  开始快,后面慢

easeInOut  开始慢,然后加速,最后再减速

*

* 当然我们也可以创建自己Curve,例如我们定义一个正弦曲线:

class ShakeCurve extends Curve {

@override

double transform(double t) {

return math.sin(t * math.PI * 2);

}

}

* */

final CurvedAnimation curve =CurvedAnimation(

parent:controller!, curve:Curves.linear);

/*

* Animation

*Animation是一个抽象类,它本身和UI渲染没有任何关系,

* 而它主要的功能是保存动画的插值和状态;其中一个比较常用的Animation类是Animation。

* Animation对象是一个在一段时间内依次生成一个区间(Tween)之间值的类。

* Animation对象在整个动画执行过程中输出的值可以是线性的、曲线的、一个步进函数或者任何其他曲线函数等等,

* 这由Curve来决定。 根据Animation对象的控制方式,

* 动画可以正向运行(从起始状态开始,到终止状态结束),

* 也可以反向运行,甚至可以在中间切换方向。

* Animation还可以生成除double之外的其他类型值

* ,如:Animation 或Animation。

* 在动画的每一帧中,我们可以通过Animation对象的value属性获取动画的当前状态值。

#动画通知

我们可以通过Animation来监听动画每一帧以及执行状态的变化,Animation有如下两个方法:

addListener();它可以用于给Animation添加帧监听器,

* 在每一帧都会被调用。

* 帧监听器中最常见的行为是改变状态后调用setState()来触发UI重建。

addStatusListener();

* 它可以给Animation添加“动画状态改变”监听器;

* 动画开始、结束、正向或反向(见AnimationStatus定义)时会调用状态改变的监听器。

* */

// 创建从50到200线性变化的Animation对象

// 普通动画需要手动监听动画状态,刷新UI

animation =Tween(begin:10.0, end:200.0).animate(curve)

..addListener(()=setState((){}));

/*

* Tween

* 默认情况下,AnimationController对象值的范围是[0.0,1.0]。

* 如果我们需要构建UI的动画值在不同的范围或不同的数据类型,

* 则可以使用Tween来添加映射以生成不同的范围或数据类型的值。

*Tween构造函数需要begin和end两个参数。

* Tween的唯一职责就是定义从输入范围到输出范围的映射。

* 输入范围通常为[0.0,1.0],但这不是必须的,我们可以自定义需要的范围。

* */

// 启动动画

controller!.repeat(reverse:true);

//

// 第二段

// animation!.addStatusListener((status) {

//  if (status == AnimationStatus.completed) {

//    controller!.reverse();// 动画结束时反向执行

//  } else if (status == AnimationStatus.dismissed) {

//    controller!.forward();// 动画反向执行完毕时,重新执行

//  }

// });

// controller!.forward();// 启动动画

}

@override

Widget build(BuildContext context) {

return MaterialApp(

home:Scaffold(

body:Center(

child:Container(

width:animation!.value,// 将动画的值赋给 widget 的宽高

              height:animation!.value,//

              child:FlutterLogo(),

)

)

)

);

}

@override

void dispose() {

// 释放资源

controller!.dispose();

super.dispose();

}

}


网站题目:flutter之,flutter百度百科
本文网址:http://scyanting.com/article/dsdeopi.html