Flutter 基础Widget

万事皆 Widget

Widget 是每个 Flutter 应用的基础。每个 Widget 是一部分用户界面上不可变的定义。和其他框架把 View、controller、 Layout 和其他资源分开定义不一样,Flutter 具有一致的、唯一的对象模型: Widget。

一个 Widget 可以定义:

  • 一个结构性的元素(比如 按钮或者菜单)
  • 一个元素的风格(比如 字体或者颜色)
  • 指定布局属性(比如 padding)
  • 也可以包含一些业务逻辑
  • 以及其他…

组合大于继承(Composition > inheritance)

Widget 通常通过组合的方式来构建复杂的 UI。例如,常用的 Container Widget 就是由几个分别负责 布局、绘制、布局和计算大小的 Widget 组成。具体来说,Container 由

  • LimitedBox,
  • ConstrainedBox,
  • Align,
  • Padding,
  • DecoratedBox,
  • Transform

等widget 组成。如果要自定义 Container 来实现自定义效果,相比使用继承而言,可以使用组合一些简单的 Widget 实现自定义效果。

[图片上传失败...(image-84ef2a-1543882561066)]

基础 Widget

主要文章: widget概述-布局模型

Flutter有一套丰富、强大的基础widget,其中以下是很常用的:

  • Text:该 widget 可让创建一个带格式的文本。

  • Row、 Column: 这些具有弹性空间的布局类Widget可让您在水平(Row)和垂直(Column)方向上创建灵活的布局。其设计是基于web开发中的Flexbox布局模型。

  • Stack: 取代线性布局 (译者语:和Android中的LinearLayout相似),Stack允许子 widget 堆叠, 你可以使用 Positioned 来定位他们相对于Stack的上下左右四条边的位置。Stacks是基于Web开发中的绝度定位(absolute positioning )布局模型设计的。

  • Container: Container 可让您创建矩形视觉元素。container 可以装饰为一个BoxDecoration, 如 background、一个边框、或者一个阴影。 Container 也可以具有边距(margins)、填充(padding)和应用于其大小的约束(constraints)。另外, Container可以使用矩阵在三维空间中对其进行变换。

flutter提供了友好的Widget 的查找地址,可以方便的找到自己组要的组件:

Stateful(有状态) 和 Stateless(无状态) widgets

什么是重点?

  • 有些widgets是有状态的, 有些是无状态的
  • 如果用户与widget交互,widget会发生变化,那么它就是有状态的.
  • widget的状态(state)是一些可以更改的值, 如一个slider滑动条的当前值或checkbox是否被选中.
  • widget的状态保存在一个State对象中, 它和widget的布局显示分离。
  • 当widget状态改变时, State 对象调用setState(), 告诉框架去重绘widget.
  • stateless widget 没有内部状态. Icon、 IconButton, 和Text 都是无状态widget, 他们都是StatelessWidget的子类。

  • stateful widget 是动态的. 用户可以和其交互 (例如输入一个表单、 或者移动一个slider滑块),或者可以随时间改变 (也许是数据改变导致的UI更新). Checkbox, Radio, Slider, InkWell, Form, and TextField 都是 stateful widgets, 他们都是 StatefulWidget的子类。

创建一个有状态的widget

  • 要创建一个自定义有状态widget,需创建两个类:StatefulWidget和State
  • 状态对象包含widget的状态和build() 方法。
  • 当widget的状态改变时,状态对象调用setState(),告诉框架重绘widget

State的状态管理

  • 有多种方法可以管理状态.
  • 选择使用何种管理方法
  • 如果不是很清楚时, 那就在父widget中管理状态吧.

一下是常见状态管理的几种方法

如何决定使用哪种管理方法?以下原则可以帮助您决定:

  • 如果状态是用户数据,如复选框的选中状态、滑块的位置,则该状态最好由父widget管理
  • 如果所讨论的状态是有关界面外观效果的,例如动画,那么状态最好由widget本身来管理.
  • 如果有疑问,首选是在父widget中管理状态

Flutter通信机制 使用平台通道编写平台特定的代码

平台方法调用
平台方法调用主要分四步:

  1. 定义好交互的协议名称以及参数名称(channelName 、methodName、parameter)
  2. 对应平台工程添加对应的平台调用代码
    • 实现MethodChannel.MethodCallHandler接口
    • onMethodCall() 中根据参数做对应处理
  3. 对应平台界面对应位置注册
  4. flutter中调用
    • 创建MethodChannel(channelName)
    • MethodChannel.invoke(methodName)

平台事件调用
平台事件调用和方法调用类似,步骤如下:

  1. 定义好交互的协议名称以及参数名称(eventlName)
  2. 对应平台工程添加对应的平台调用代码
    • 实现EventChannel.StreamHandler接口
    • 在onListen中的EventChannel.EventSink eventSink可以用来发送事件的结果
  3. 对应平台界面对应位置注册
  4. flutter注册事件
    • 创建EventChannel(eventlName)
    • 开始监听eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);

除了上面提到的MethodChannel,你还可以使用BasicMessageChannel,它支持使用自定义消息编解码器进行基本的异步消息传递。 此外,您可以使用专门的BinaryCodecStringCodecJSONMessageCodec类,或创建自己的编解码器。

Flutter 图片

在Flutter中系统为我们提供了可以加载图片的控件Image,Image 控件提供了如下几种用于加载不同方式的图片。

  • new Image, 用于从ImageProvider获取图像。
  • new Image.asset, 用于从AssetBundle获取图像。
  • new Image.network, 用于从URL获取图像。
  • new Image.file, 用于从文件中获取图像。
  • new Image.memory, 用于从内存中获取图像

在flutter中Image支持JPEG, PNG, GIF, Animated GIF, WebP, Animated WebP, BMP, 和 WBMP这几种图片格式。
今天我们主要介绍下两种常用的方式,从asset目录和从network获取图片

从asset目录加载图片

1. 新建存放图片的目录
    想要使Image加载asset加载apk内部的图片,首先需要在项目中建立放置图片的目录(名称随意,不要中文就好)
我们在lib目录的同级目录建立images文件夹,并在里面放置了一个cover.jpg的图片

2. 更细配置文件pubspec.yaml
    在flutter节点下新增 文件声明 ,如下所示
    flutter:
    assets:
     - images/cover.jpg
    
3. 加载目录中的图片文件
    new Image.asset("images/helloflutter.png")

从network获取图片

基本上和上面asset的参数相同,只不过从网络获取的方式可以传入请求头。

但是,从我网络获取图片展示的调用方式就比从asset读取显示方便的多,只需要一行代码就可以搞定
把Scaffold body的参数写为:

new Image.network("http://pic1.win4000.com/wallpaper/2017-10-25/59f083092ed4f.jpg")

Flutter 网络

注:本篇文档官方使用的是用dart io中的HttpClient发起的请求,但HttpClient本身功能较弱,很多常用功能都不支持。我们建议您使用dio 来发起网络请求,它是一个强大易用的dart http请求库,支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载……详情请查看github dio

处理异步

注意,HTTP API 在返回值中使用了Dart Futures。 我们建议使用async/await语法来调用API。

网络调用通常遵循如下步骤:

  • 创建 client.
  • 构造 Uri.
  • 发起请求, 等待请求,同时您也可以配置请求headers、 body。
  • 关闭请求, 等待响应.
  • 解码响应的内容.
get() async {
  var httpClient = new HttpClient();
  var uri = new Uri.http(
      'example.com', '/path1/path2', {'param1': '42', 'param2': 'foo'});
  var request = await httpClient.getUrl(uri);
  var response = await request.close();
  var responseBody = await response.transform(UTF8.decoder).join();
}

Flutter给我们提供了第三发库的支持,同样的下面三个操作

  • 打开项目的pubspec.yaml配置我文件在dependencies:节点下新增如下配置
    http: ^0.11.3+16
  • 点击开发工具提示的packages get按钮或者在命令行输入flutter packages get来同步第三方插件
  • 在自己的Dart文件中引入插件即可正常使用了
    • import ‘package:http/http.dart’ as http
var url = "http://example.com/whatsit/create";
http.post(url, body: {"name": "doodle", "color": "blue"})
    .then((response) {
  print("Response status: ${response.statusCode}");
  print("Response body: ${response.body}");
});

官网还退出了一个网络第三方库 github dio

Dio dio = new Dio();

  _loadDataByDio() async {
    try {
      Response response = await dio.get("https://news-at.zhihu.com/api/4/news/latest");
      if (response.statusCode == HttpStatus.ok) {
        _result = response.data.toString();
      } else {
        _result = 'error code : ${response.statusCode}';
      }
    } catch (exception) {
      print('exc:$exception');
      _result = '网络异常';
    }

    setState(() {});
  }

Flutter路由

静态路由

在Flutter中有着两种路由跳转的方式,一种是静态路由,在创建时就已经明确知道了要跳转的页面和值。另一种是动态路由,跳转传入的目标地址和要传入的值都可以是动态的。

OK,还是先来介绍下静态路由

从我们开始学习Flutter到现在,相信大家看到最多的肯定是下面的代码

void main() {
  runApp(new MaterialApp(
    home: new MyApp(),
    routes: <String, WidgetBuilder>{
      '/page2': (BuildContext context) => new Page2("requestString"),
    },
  ));
}

routes: const {}

routes需要传入类型的Map,第一个参数是目标路由的名称,第二个参数就是你要跳转的页面。

这种定义路由并使用的方式非常的简单,但是大家肯定会发现一个问题,就是如果我需要传递给第二个页面的数据不是已知的话我就无法使用这种方式,因为我们无法动态改变上面定义的值。

所以,我们就需要了解下Flutter中的动态路由了。

动态路由

在Navigator中还有一个方法是push()方法,需要传入一个Route对象,在Flutter中我们可以使用PageRouteBuilder来构建这个Route对象。

Navigator.of(context).push(new PageRouteBuilder(
                        pageBuilder: (BuildContext context,
                            Animation<double> animation,
                            Animation<double> secondaryAnimation) {
                          return new Page2("some attrs you like ");

                        }))

这样的话,我们就可以把用户操作与交互的数据传递给下个页面。

页面出栈

在Flutter中我们可以使用Navigator.of(context).pop()进行出栈操作,但是值得注意的时如果页面上有Dialog、BottomSheet、popMenu类似的Widget使用pop()方法会优先进行这些Widget的关闭操作。

处理出栈页面返回值

在前面我们介绍到Navigator.of(context).pop()可以使得页面出栈,当然这个pop方法也是可以传值的,只用Navigator.of(context).pop(attrs)就可以传入自己想要返回的值

Future future = Navigator.of(context).pushNamed("/pageB");
            future.then((value) {
              showDialog(
                  context: context,
                  child: new AlertDialog(
                    title: new Text(value),
                  ));
            }

小结

  • 使用Navigator.of(context).pushName(“/“)可以进行静态路由的跳转
  • 使用push(Route)可以进行态路由的跳转
  • 动态路由可以传入未知数据
  • 使用pop()可以进行路由的出栈并且可以传递参数
  • 可以使用Future接收上个页面返回的值。

Flutter插件

官网提供了很多好用的插件,插件地址

  1. flutter_image
    • 使用NetworkImageWithRetry 代替Image.network 加载网络图片可获得重试能力。
  2. barcode_scan
    • 一个可以扫描二维码和条形码的flutter插件。
  3. intl
    • 该插件提供国际化和本地化设施,包括消息翻译,复数和性别,日期/数字格式和解析以及双向文本。
  4. location
    • 这个插件 能够处理Android和iOS设备的位置。它还提供位置更改时的回调。

等等。。。其他科自行搜索使用。
如果你想开发一个新的插件可以参考开发Packages和插件

项目地址 FlutterDemo

参考

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容