资讯

精准传达 • 有效沟通

从品牌网站建设到网络营销策划,从策略到执行的一站式服务

Flutter手册.pdf,flutter基础教程

PDF文件生成

PDF(Portable Document Format)是Adobe公司发明的一种文档格式,由于其具有很多独特的优点而被广泛使用。如pdf可内嵌字体,这样就可以避免客户端没有安装字体而显示不一致;如pdf的图片和文字使用了矢量图,这样就可以随意放大而不会失真;另外pdf的加密和防篡改也是一大亮点,是向外发布资料的首选格式

创新互联建站-专业网站定制、快速模板网站建设、高性价比建始网站开发、企业建站全套包干低至880元,成熟完善的模板库,直接使用。一站式建始网站制作公司更省心,省钱,快速模板网站建设找我们,业务覆盖建始地区。费用合理售后完善,10余年实体公司更值得信赖。

一个未经修改的PDF文件从头到尾主要包括4个部分,分别是:文件头、对象集合、交叉引用表、文件尾。其中:

%PDF-1.4

1 0 obj

/Producer (Skia/PDF m92)

endobj

xref

0 83

0000000000 65535 f

0000000015 00000 n

0000010954 00000 n

trailer

/Size 83

/Root 11 0 R

/Info 1 0 R

startxref

50152

%%EOF

iOS可以通过UIGraphicsPDFRenderer类生成PDF,其本身的api非常简单:一个init方法,一个写入文件的方法,一个导出data数据的方法

用于构造UIGraphicsPDFRenderer,第一个参数是pdf的尺寸,第二个参数可以设置pdf文件的元数据

生成pdf并写入到指定URL

生成pdf并返回Data

绘制PDF主要依靠 UIGraphicsPDFRendererContext ,这是UIGraphicsRendererContext的子类,所以iOS是使用CoreGraphics的draw api进行pdf绘制的

除了CoreGraphics的相关api之外,最重要的是 func beginPage() ,用于创建一页pdf

安卓可以使用 PdfDocument 类生成PDF,和iOS类似,采用了系统的绘图api( Canvas ),对于开发者来说学习成本很低。但是安卓的坑比较多,建议采用iText、PDFBox等第三方实现。如drawText不支持多行文本,要通过较复杂的操作来实现;某些系统对文档内的图片不进行压缩,导致生成的pdf比正常的大10多倍

flutter可以使用 pdf库 生成pdf,该库实现了一套自己的widgets,开发者可以像写普通widgets一样去写pdf;另外还提供了table相关的api,不用手动画表格,还支持自动分页,非常友好。

系统找不到指定的路径怎么解决?

1.首先,请按“Win+R”键输入“Services.msc”回车打开服务;

2.以Windows Firewall为例,右击点击属性;

3.即可查看服务对应的可运行文件路径;

4.接着回到桌面,按“Win+R”键输入“regedit”回车打开注册表;

5.接下来定位到HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/services/MpsSvc;

6.双击并确认ImagePath的数值数据是否相同,如"%SystemRoot%/system32/svchost.exe

-k LocalServiceNoNetwork";

7.如不同,可设置相同完成后重新启动计算机,即可以解决系统找不到指定路径”问题。

Flutter PDF阅读,可显示页数,源码

添加flutter_pdfview: ^1.2.1 组件

class PDFScreenextends StatefulWidget {

final Stringurl;

final Stringpath;

final Stringtitle;

PDFScreen({Key key,this.url, this.path, this.title}) :super(key: key);

_PDFScreenStatecreateState() =_PDFScreenState();

}

class _PDFScreenStateextends Statewith WidgetsBindingObserver {

final Completer_controller =

Completer();

intpages =0;

intcurrentPage =0;

boolisReady =false;

StringerrorMessage ='';

@override

Widgetbuild(BuildContext context) {

return Scaffold(

appBar:AppBar(

elevation:0,

    leading:new IconButton(

icon:Image.asset(

Utils.getImgPath('icon_back'),

        width:18,

        height:36,

      ),

      onPressed: () {

Navigator.of(context).pop();

      },

    ),

    centerTitle:true,

    title:Text(

widget.title,

      style:TextStyle(fontSize:17.0),

    ),

  ),

  body:Stack(

children: [

Positioned(

height: MediaQuery.of(context).size.height - (Utils.getHeightSize(80, context) *2),

          width: MediaQuery.of(context).size.width,

          child:PDFView(

filePath:widget.path,

            enableSwipe:true,

            swipeHorizontal:true,

            autoSpacing:false,

            pageFling:true,

            pageSnap:true,

            defaultPage:currentPage,

            fitPolicy: FitPolicy.BOTH,

            preventLinkNavigation:

false, // if set to true the link is handled in flutter

            onRender: (_pages) {

setState(() {

pages = _pages;

                isReady =true;

              });

            },

            onError: (error) {

setState(() {

errorMessage = error.toString();

              });

              print(error.toString());

            },

            onPageError: (page, error) {

setState(() {

errorMessage ='$page: ${error.toString()}';

              });

              print('$page: ${error.toString()}');

            },

            onViewCreated: (PDFViewController pdfViewController) {

_controller.complete(pdfViewController);

            },

            onLinkHandler: (String uri) {

print('goto uri: $uri');

            },

            onPageChanged: (int page, int total) {

print('page change: $page/$total');

              setState(() {

currentPage = page;

              });

            },

          ),

      ),

      Positioned(

bottom:0,

          height: Utils.getHeightSize(80, context),

          width: MediaQuery.of(context).size.width,

          child:Container(

// padding: EdgeInsets.only(left: 10.0, right: 10.0,top: 10.0,bottom: 10.0),

            decoration:BoxDecoration(

color: Colors.white,

              border:Border.all(color: AppColors.shadeGary),

              boxShadow: [

//refer to :

                BoxShadow(

color: AppColors.shadeGary,

                    offset:Offset(0.0, 0.0),

                    blurRadius:3.0,

                    spreadRadius:0.0),

              ],

            ),

            child:Stack(

children: [

Row(

mainAxisSize: MainAxisSize.min,

                  children: [

Container(),

                    Expanded(child:SizedBox()),

                    Container(

height:42.0,

                      width: Utils.getWidthSize(90, context),

                      margin:EdgeInsets.only(right:20.0,bottom:5.0),

                      decoration:BoxDecoration(//边框线

                        borderRadius:BorderRadius.circular(21.0),  //圆角

                        gradient:LinearGradient(

colors: [Color(0xFF5FD27A), Color(0xFF3FAF6F)],

                        ),

                      ),

                      child:TextButton(

style:ButtonStyle(

overlayColor: MaterialStateProperty.all(Colors.transparent),

                          foregroundColor: MaterialStateProperty.resolveWith(

(states) {

if (states.contains(MaterialState.pressed)) {

//按下时的颜色

                                return Colors.transparent;

                              }

//默认状态使用灰色

                              return Colors.transparent;

                            },

                          ),

                        ),

                        child:Text(

globalTranslations.text("msg_download"),

                          style:TextStyle(color: Colors.white),

                        ),

                        onPressed: () {

launchPdfURL(widget.url);

                        },

                      ),

                    ),

                  ],

                ),

              ],

            ),

          )),

      errorMessage.isEmpty

          ? !isReady

          ?Center(

child:CircularProgressIndicator(),

      )

:Container()

:Center(

child:Text(errorMessage),

      )

],

  ),

  // floatingActionButton: FutureBuilder(

//  future: _controller.future,

//  builder: (context, AsyncSnapshot snapshot) {

//    if (snapshot.hasData) {

//      return FloatingActionButton.extended(

//        label: Text("Go to ${pages ~/ 2}"),

//        onPressed: () async {

//          await snapshot.data.setPage(pages ~/ 2);

//        },

//      );

//    }

//

//    return Container();

//  },

// ),

);

}

launchPdfURL(String url) {

launch(url);

}

}

Android真的推荐用MVI模式?MVI和MVVM有什么区别?

android自己卷自己,自己造一个MVI架构模式吗?

MVI架构模式是国内android开发者最近一两年造出来的吗?

看了很多MVI的资料,发现都提到cycle.js框架。android的mvi架构就是启发于cycle.js框架。

我们再看看Cycle.js框架是什么时候开始的,又是什么时候开始使用MVI模式的。

Cycle.js框架 第一个预发版本 :

再结合官方文档来看,Cycle.js框架就是为了MVI架构模式而生的。

虽然不知道,Cycle.js框架是不是首个MVI模式框架。

但是从很多资料可以推测,MVI架构模式就是Cycle.js框架推广开来的。

而且早在2014年就已经在前端开发中用得飞起了。

想想2014年,咱们在干嘛?android在用什么架构模式。

正所谓,天下武功出少林啊。

我们android的很多技术,在前端早就用“烂了”。

我们知道MVP和MVVM的爹都是MVC。MVI的爹也是MVC。

MVC的Controller是命令是编程组件,不能直接实现响应式编程思想。

响应式编程范式(Reactive programming):

安卓官方的compose框架、微信小程序、Flutter、React、鸿蒙UI的开发框架,都是使用响应式开发框架。

这里就不拓展开来讲了,上面提到的任何一个开发框架,你只要会一个基本就能理解响应式编程范式。

如果一个都不会也没关系,现在不理解响应式编程也没关系,等你学会MVI就理解了,这种只有实际使用过才能深刻理解。

学不会也没关系,不要焦虑(尤其那些工作不久的小伙伴,学不会属于正常现象~)

MVI,咱第一遍学不会,就等2年,再学一遍~

2年后也没学会,那就再等2年~ 一定要有耐心~

如果还是学不会,那也没关系,因为MVI早晚也会过时~ 等过时了就不用学了~

哈哈哈~ 别笑,正经Android可不会开玩笑的。

就像rxjava,当年有多少人死活学不会,android开发现在谁还学Rxjava?哈哈哈~

用一张图来总结这次升级的核心思想:

新版架构指南在旧版的基础上,做了如下调整和建议:

1. 将LiveData组件改成了StateFlow

对协程的使用更友好。并且更能体现面向数据流开发的思想。

实际上,依然使用LiveData也没毛病。

2. ViewModel传递给View的数据限制为View的UIState

ViewModel从Model层获取数据后,转换为UIState数据,通过StateFlow流向View层。

UIState的数据面向界面组件而定义的,是能直接控制View组件如何显示的数据。

所以我们也可以称UIState为界面的状态或者View的状态。

如下:

3. 单数据流还是多数据流的选择

官方指南并没有强制我们使用单流。

同一个界面应该使用单个StateFlow还是多个StateFlow,需要我们自己判断。

我们应该根据UIStates数据们之间关联程度来决定多流还是单流。

单流优缺点都十分明显:

优点: 数据集中管控,会提高代码的可读性和修改的便利性。

缺点: 当数据非常多且复杂时,会影响效率。因为我们没有diff功能,View层不能只更新有变化的数据,只会根据UIState刷新当前界面。

我们再看下官方新版架构图:

当然不仅仅MVVM可以改造成响应式开发范式,MVP也是可以的。

不信你看 这篇blog :

1. 理解MVC架构模式的思想【MVC是其他架构模式之爹,他的思想是MVP、MVVM、MVI的基础,学会它是关键步骤~】。

3. 学习kotlin的StateFlow组件,的使用:Sequence-Flow-StateFlow。

4. 学习ViewModel组件的使用(虽然不用ViewModel也能实现MVI架构,但是ViewModel还是值得学习)。

5. 理解DRY(Don't Repeat Yourself)原则。

6. 理解MVVM(因为官方的MVI模式是基于MVVM的基础改造的~)。

7. 学习官方架构指南。

8. 实战。

在这里就分享一份由大佬亲自收录整理的 学习PDF+架构视频+面试文档+源码笔记 , 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料

这些都是我现在闲暇时还会反复翻阅的精品资料。里面对近几年的大厂面试高频知识点都有详细的讲解。相信可以有效地帮助大家掌握知识、理解原理,帮助大家在未来取得一份不错的答卷。

当然,你也可以拿去查漏补缺,提升自身的竞争力。

真心希望可以帮助到大家,Android路漫漫,共勉!

如果你有需要的话,只需 私信我【进阶】即可获取

Flutter入门-Dart面向对象原理

Dart作为高级语言,支持面向对象的很多特性,并且支持基于mixin的继承方式,基于mixin的继承方式是指:一个类可以继承自多个父类,相当于其他语言里的多继承。所有的类都有同一个基类Object,这和特性类似于Java语言,Java所有的类也都是继承自Object,也就是说一切皆对象。

Dart 是一门面向对象的语言, 全部的类都是继承自 Object , 除了支持传统的 继承、封装、多态 , 还有基于组合(Mixin-based)的继承特性

类型推导(var/final/const)

var

final和const的区别

3.非零即真( )

4.字符串

5.集合

Dart中变量初始值为null,即使是int类型也可以是null(java中int默认是0, boolean默认是false); Dart支持自识别,可以是用var定义变量,也可以直接指定具体类型; final或者const都可修饰不可变的变量,final变量只能赋值一次,const是编译时常量。

int和double是num子类,没有float类型; 支持字符串模板,用${expression}的方式来实现字符串效果,类似如字符串拼接; String可以使用单引号或者双引号; Dart没有数组,只有列表; 其中List,Set,Map不是抽象接口,是具体实现类,可直接使用; Map的key没有指定类型,key类型不一致不会报错;key不能相同,但是value可以相同,value可以为null。 var name = 'Tom';

方法也是对象,方法可赋值给一个变量; 如果方法的参数是解构出来的可以通过 @required 注解标注为必填 const Scrollbar({Key key, @required Widget child}); 支持可选参数,可选命名参数用{}包围,可选位置参数写在最后并且使用[]包围 String say(String from, String msg, [String device]); 支持默认参数 void enableFlags({bool bold = false, bool hidden = false}) {…}; 以_开头的方法都是私有的。 void main() {

支持闭包,闭包能够访问外部方法内部的局部变量

1.空替换?? expr1 ?? expr2,如果expr1是non-null,返回其值。否则执行expr2并返回其结果; 2.条件成员访问?.P?.y = 4; 如果p是non-null,则设置y的值等于4; 3.类型判定操作符(as,is,is!); 4.级联操作,可以在同一个对象上连续调用多个函数以及访问成员变量;

和java不同的是,Dart可以抛出任意类型的对象; 程序不会强制要求开发中处理异常,但若发生异常,程序会中断; 其中异常主要分为Error和Exception两种类型。

创建对象可以不使用new关键字; Dart中没有public,private,protected这些关键字; 没有interfaces关键字,每一个类都是一个接口。我们可以用抽象类来类比java中的接口; Dart把多重继承的类叫做Mixins。

支持语法糖 Point(this.x, this.y); 每个实例变量都会自动生成一个getter方法,Non-final变量还会自动生成一个setter; 命名构造函数,使用命名构造函数可以为一个类实现多个构造函数,也能更加清晰的表明你的意图;

断言是如果条件表达式不满足则停止代码执行; 断言只在检查模式下运行有效,如果在生产模式下运行则不会执行。

Dart 以两种模式运行: Dart 1.x 有生产模式和检查模式两种模式, Dart 2.x 中移除了检查模式。

注:建议在开发/测试模式中使用 检查模式 运行 Dart VM ,因为它会添加警告和错误以帮助开发和调试过程;选中的模式会强制执行各种检查,例如类型检查等。

dart标识符可以包括字符和数字,但不能以 数字开头 。

Dart 是一种面向对象的编程语言。

代码说明:定义了一个类 TestClass ,这个类拥有一个方法 disp() ,方法可以实现在终端打印字符串 Hello Dart! ,使用 new 关键字创建类的对象,该对象调用方法 disp() 。

关于dart的学习还有很多;我列出如下: Flutter高级工程师进阶学习资料;需要可以私信我。发送“核心笔记”或“手册”,即可领取资料!

Flutter——pdf阅读功能的实现

实现pdf阅读、横竖屏切换,以及pdf页面的点击放大和双指放大等功能

在这个项目中使用的是 flutter_plugin_pdf_viewer: ^1.0.7 ,可以满足我们最基本的pdf需求阅读需求。所做的满足项目需求的工作主要是横竖屏切换功能,以及我们的初始化继续阅读等等。

首先导入插件部分源码

插件所提供的示例,已经满足了最基本的图片放大、横屏阅读的功能,我们工作的难点就在于pdf竖屏阅读的实现,所以我们需要解决的问题主要有以下几点:

(1) 横屏加载同一页面不能重复流量加载

(2) 切换竖屏时加载速度不能过慢,页面不能有断层

(3) 横竖屏切换时页码的定位保持

针对于上述问题,我们一一进行解决。

重复流量加载 ,解决这一问题比较简单,我们可以利用缓存实现,在每一次加载pdf页时,存储其(key,value),这样在下一次加载时我们会判断这个页面在缓存中是否已经存在,不存在重新加载,存在则调用缓存中的数据,页面销毁时清除所有缓存即可。

切换横竖屏 ,竖屏PDF阅读的实现,思路就是将所有横屏页面存在list中,使用LIstView.builder()进行绘制,这种方法存在的缺点就是太慢了,需要将所有页面全部加载之后,才可以绘制页面,用户体验非常差,所以我们需要做一些改进,为了提升加载速度,实现效果GIF中的效果,我们就要使用FutureBuilder()方法,来实现预加载功能,具体实现如下:

(在这里不对此组件过多介绍,后续会专门介绍此组件的使用),这样我们就可以实现预加载的功能了。

横竖屏切换定位 ,这个点的解决思路已经在我的 (Flutter 初始化ListView定位子组件位置) 中进行了介绍,实现了解决。

至此,我们就解决了所有的难点问题。


网站栏目:Flutter手册.pdf,flutter基础教程
网页链接:http://cdkjz.cn/article/phjcgo.html
多年建站经验

多一份参考,总有益处

联系快上网,免费获得专属《策划方案》及报价

咨询相关问题或预约面谈,可以通过以下方式与我们联系

业务热线:400-028-6601 / 大客户专线   成都:13518219792   座机:028-86922220