UI综合案例

  • 屏幕适配
  • 自定义AppBar
  • 多类型列表项
  • 悬浮框控件
  • 第三方库使用
  • 状态管理框架

本案例需要去 Github 仓库 下载测试服务器,按要求配置

屏幕适配

分辨率

分辨率,又称解析度、解像度,可以细分为显示分辨率、图像分辨率、打印分辨率和扫描分辨率等。

显示分辨率(屏幕分辨率)是屏幕图像的精密度,是指显示器所能显示的像素有多少。由于屏幕上的点、线和面都是由像素组成的,显示器可显示的像素越多,画面就越精细,同样的屏幕区域内能显示的信息也越多,所以分辨率是个非常重要的性能指标。可以把整个图像想象成是一个大型的棋盘,而分辨率的表示方式就是所有经线和纬线交叉点的数目。显示分辨率一定的情况下,显示屏越小图像越清晰,反之,显示屏大小固定时,显示分辨率越高图像越清晰。

1080p和720p是什么意思

P指逐行扫描,1080p指横向或纵向达到1080个像素,如果屏幕宽高是16:9,那就是1920X1080。同理,720p就是1280X720分辨率。

计算图片在内存中的大小

高 * 宽 * 像素占用的字节数

一个像素占用的字节数与图片的编码格式有关,通常的ARGB_4444每个用4位,共占用两字节,ARGB_8888则每个用8位,共占四字节。

屏幕大小

大家平常说的手机屏幕几寸,是指屏幕对角线的长度,单位是英寸。

常见单位

  • in(inch 英寸):物理长度单位。是英国(英联邦)及其前殖民地的长度单位,1英寸=2.54厘米
  • px(pixel 像素):,电子屏幕上组成一幅图画或照片的最基本单元
  • pt(point 点):原印刷行业常用单位,等于1/72英寸,ios开发使用
  • ppi(pixel per inch):每英寸包含的像素个数,该值越高,则屏幕越细腻
  • dpi(dot per inch):每英寸包含多少点,该值越高,则图片越细腻
  • dp(dip,Density-independent pixel):,独立密度像素。意即与density密度(dpi)无关。是安卓开发用的长度单位,使用dp作为单位,不用操心屏幕大小,屏幕的dpi,它显示的效果始终保持一致。
  • sp(scale-independent pixel):安卓开发中用的字体大小单位

dpi最初用于衡量打印物上每英寸的点数密度。值越小图片越不精细。当dpi的概念用在计算机屏幕上时,就应称之为ppippi就是计算机屏幕上每英寸可以显示的像素点的数量,因此,ppidpi是等同的。

转换公式

dpi = ppi

1pt = (dpi / 72) px

1dp =(dpi / 160)px

安卓规格

为什么规定在160dpi的屏幕上,1dp = 1px呢?

因为第一款Android设备(HTC的T-Mobile G1)是属于160dpi。

安卓适配原理

px = dp * (dpi / 160) = dp * (屏幕对角线像素 / in(屏幕尺寸) / 160)

dp不变且in不变时,屏幕对角线像素数量越大,px越大。

假如不使用dp,而使用原始的px单位,例如在手机屏幕上绘制一条直线,如果在密度为160dpi(每英寸160个像素点),宽度是1英寸的手机上,这条直线长度是160px(占据160个像素点),也就是直线长度正好是手机的宽度,但在240dpi(每英寸240个像素点),宽度是1英寸的手机上安装了这个app,长度只有屏幕宽度的2/3。

iPhone规格

设备 屏幕尺寸 分辨率(pt) Reader 分辨率(px) 渲染后 PPI
iPhone 3GS 3.5吋 320x480 @1x 320x480 163
iPhone 4/4s 3.5吋 320x480 @2x 640x960 330
iPhone 5/5s/5c 4.0吋 320x568 @2x 640x1136 326
iPhone 6 4.7吋 375x667 @2x 750x1334 326
iPhone 6Plus 5.5吋 414x736 @3x 1242x2208 1080x1920 401
iPhone 6s 4.7吋 375x667 @2x 750x1334 326
iPhone 6Plus 5.5吋 414x736 @3x 1242x2208 1080x1920 401
iPhone 7 4.7吋 375x667 @2x 750x1334 326
iPhone 7Plus 5.5吋 414x736 @3x 1242x2208 1080x1920 401
iPhone 8 4.7吋 375x667 @2x 750x1334 326
iPhone 8Plus 5.5吋 414x736 @3x 1242x2208 1080x1920 401
iPhone X 5.8吋 375x812 @3x 1125x2436 458

Flutter 规格

Flutter中为了处理屏幕的适配,使用逻辑像素的概念,见官方文档 devicePixelRatio,在不同手机上,Flutter框架会动态的将逻辑像素换算成真实的物理像素。逻辑像素的概念与安卓中的dp类似,但概念不能混淆,关于该值的详细内容,参见我的博客《Flutter 大小单位详解》

物理像素 = devicePixelRatio * 逻辑像素

但实际开发过程中,往往需要直接将设计师给出的尺寸标注换算成Flutter的逻辑像素。这个动态换算的过程,我们可以使用第三方库 flutter_screenutil 来简化编码。

悬浮框

    OverlayState overlayState = Overlay.of(this.ctx);

    // 创建OverlayEntry
    OverlayEntry entry = OverlayEntry(builder:(ctx){
      return Container();
    });
    // 往 Overlay 中插入插入OverlayEntry
    overlayState.insert(overlayEntry);
   // 移除
    entry.remove();

第三方库使用

flutter_staggered_grid_view (交错网格)的使用

构建方式

  • StaggeredGridView.count 构建一个横轴方向固定Tile个数的网格
  • StaggeredGridView.countBuilder 同上,适用于动态构建的情景
  • StaggeredGridView.extent 构建一个横轴方向上固定最大宽度的Tile网格
  • StaggeredGridView.extentBuild 同上,适用于动态构建的情景

StaggeredTile 的构建

  • StaggeredTile.count 固定横轴和主轴上的数量(设置横轴和主轴占据的单元数)
  • StaggeredTile.extent 设置横轴上的数量和主轴上的最大范围
  • StaggeredTile.fit 设置横轴上的数量,而主轴上则自适应

需要注意,StaggeredGridView的列数,等于crossAxisCount除以StaggeredTile上设置的横轴的数量。

状态管理框架 Provider

现有的 providers

provider 为不同类型的对象暴露了几种不同的 "provider"。provider 文档

所有可用对象的完整清单见这里

名字 描述
Provider provider的最基本形式。它接受一个值并将其公开,不管这个值是什么。
ListenableProvider 一个特定的Listenable对象的provider。ListenableProvider将监听对象,并要求依赖它的widget在监听器被调用时进行重建。
ChangeNotifierProvider 用于ChangeNotifier的ListenableProvider的规范。 它将在需要时自动调用ChangeNotifier.dispose
ValueListenableProvider 监听一个ValueListenable,并且只暴露ValueListenable.value
StreamProvider 监听一个流,并暴露出最新发出的值。
FutureProvider 接受Future并在Future完成时更新依赖关系。

需要注意,从3.0.0版本开始,有了一种新的Provider——ProxyProvider。它是一个将其他Provider的多个值合并到一个新对象中的Provider,并将结果发送给这个 Provider。每当它所依赖的一个Provider更新时,这个新对象就会被更新。

  • 声明数据 Model
  • 创建共享数据
  • 获取共享数据

获取共享数据有几种方式

  • 使用Provider.of<T>(context)获取指定泛型的实例对象
  • 使用 Consumer 获取
  • 使用Selector获取
  • 使用扩展方法context.watch/read/select获取

聊天室案例

主要掌握Flutter中WebSocket的使用

服务端

json 模型

{
    "event": "message",
    "data": {
        "type": "text",
        "content": "",
        "from": ""
    }
}

event类型:message/name/name_ack/broadcast/error/keepalive

Flutter 客户端

  • 使用web_socket_channel

    Flutter中用于websocket 访问的库

  • 为 websocket 添加心跳机制

  • 使用 protobuf 改造案例

    全称是Google Protocol Buffer,是一种高效轻便的结构化数据存储方式,可用于(数据)通信协议、数据存储等。它与语言无关,平台无关,且高效性能好。

Protobuf 语法简介

Protobuf 使用.proto文件来定义消息,一个简单示例如下

// 语法版本
syntax = "proto3";

message SearchRequest {
  string query = 1;
  int32 page = 2;
  int32 result = 3;
}

整体看起来和Dart中的类声明相似,message后面指定的名称,会映射成Dart的类名。消息定义中的每个字段都有一个唯一的数字 ,这是字段编号,这些字段编号用于以消息二进制格式标识字段,一旦消息类型被使用,就不应该更改这些字段。

消息嵌套

syntax = "proto3";

message SearchRequest {
  string query = 1;
  int32 page = 2;

  // 声明Result类型消息
  message Result {
    string url = 1;
    string title = 2;
  }

  // 在SearchRequest中定义一个Result字段
  Result result = 3;
}

这些消息中还能定义枚举。枚举的第一个常量编号映射到零,意即必须有一个字段编号为0的值

syntax = "proto3";

message SearchRequest {
  string query = 1;
  int32 page = 2;
  int32 result = 3;
  // 使用snippets修改,声明列表,此处为字符串列表
  repeated string snippets = 3;

  // 声明一个枚举类型
  enum Corpus {
    UNIVERSAL = 0;
    WEB = 1;
    IMAGES = 2;
    LOCAL = 3;
  }

  // 在SearchRequest中定义一个枚举字段
  Corpus corpus = 4;
}

在Dart 中的使用

  1. 下载protobuf 工具

    工具链接

  2. 安装dart语言的protoc插件

    pub global activate protoc_plugin
    

    安装插件后,需要将该插件路径配置到PATH环境变量中,Windows下是%APPDATA%\Pub\Cache\bin,Mac 或 Linux系统下是$HOME/.pub-cache/bin

  3. .proto文件编译成dart文件

    protoc --dart_out=. test.proto
    

    将生成的带pb的dart文件拷贝到项目中

  4. 项目中引入protobuf 依赖

     dependencies:
       protobuf: ^1.0.1
    

附录:字段类型对应表

.protoNotesJavaPython[2]GoDart
doubledoublefloatfloat64double
floatfloatfloatfloat32double
int32使用可变长度编码。编码负数效率低,如果您的字段可能具有负值,请改用sint32。intintint32int
int64使用可变长度编码。负数编码效率低下,如果您的字段可能具有负值,请改用sint64。longint/long[3]int64Int64
uint32使用可变长度编码。int[1]int/long[3]uint32int
uint64使用可变长度编码。long[1]int/long[3]uint64Int64
sint32使用可变长度编码。有符号的int值。这些比普通的int32更有效地编码负数。intintint32int
sint64使用可变长度编码。有符号的int值。这些比普通的int64更有效地编码负数。longint/long[3]int64Int64
fixed32始终为四个字节。如果值通常大于228,则比uint32更有效。int[1]int/long[3]uint32int
fixed64始终为八个字节。如果值通常大于256,则比uint64更有效。long[1]int/long[3]uint64Int64
sfixed32始终为四个字节。intintint32int
sfixed64始终为八个字节。longint/long[3]int64Int64
boolbooleanboolboolbool
string字符串必须始终包含UTF-8编码或7位ASCII文本,并且不能超过232。Stringstr/unicode[4]stringString
bytes可以包含不超过232个任意字节序列。ByteStringstr[]byteList

[1]在Java中,无符号的32位和64位整数使用带符号的对等体表示,最高位仅存储在符号位中。

[2]在所有情况下,对字段设置值都会进行类型检查以确保其有效。

[3]64位或无符号32位整数在解码时始终表示为long,但是如果在设置字段时给出了int,则可以为int。 在所有情况下,该值都必须适合设置时表示的类型。 参见[2]。

[4]Python字符串在解码时表示为unicode,但如果给出了ASCII字符串,则可以为str(此字符串可能会发生变化)。


公众号“编程之路从0到1”

20190301102949549

Copyright © Arcticfox 2020 all right reserved,powered by Gitbook文档修订于: 2022-05-01 12:00:54

results matching ""

    No results matching ""