zoukankan      html  css  js  c++  java
  • Flutter入门基础

    Flutter是Google开发的一套全新的跨平台、开源UI框架(本质上就是sdk)。 支持iOS、Android系统,并且是Fuchsia系统的默认开发套件。桌面和web上的支持也都在实验中。
    Flutter特点:跨平台(Flutter是Fuchsia的开发框架,同时支持Android、IOS),媲美原生性能,热重载(目前不支持热更新,但已加入2019工作计划)。

    其官方编程语言为Dart熟悉Dart语言

    入门网站:Flutter中文网 Flutter官网(英文)

    1、工程基础简介
    1.1 Dart导包规则

    (1)导包dart库里面的包

    import 'dart:html';
    

    (2)导入pubspec.yaml 的dependencies依赖的包

    import 'package:test/test.dart';
    

    (3)导入路径包,base为flutter根目录

    import 'package:base/components/swiper.dart';
    

    (4)只导入foo

    import 'package:lib1/lib1.dart' show foo;
    

    (5)Im除了foo都导入

    import 'package:lib2/lib2.dart' hide foo;
    

    (6)包里面存在标识符冲突

    import 'package:lib1/lib1.dart';
    import 'package:lib2/lib2.dart' as lib2;
    

    (7)延迟加载(懒加载)允许应用程序在需要时加载库。以下是一些您可能使用延迟加载的情况:
       减少应用程序的初始启动时间。
       例如,执行A / B测试 - 试用算法的其他实现。
       加载很少使用的功能,例如可选的对话框。

    import 'package:greetings/hello.dart' deferred as hello;
    
    1.2 工程配置文件:

    Flutter项目中的pubspec.yaml文件相当于Android项目中的gradle文件,项目的信息以及依赖在此文件中声明。依赖包由pub包仓库管理:https://pub.dartlang.org/ ,未发布在pub包仓库的插件可以使用本地文件路径,甚至可以直接使用git项目地址。 参考:https://flutterchina.club/using-packages/

    依赖冲突:用any来解决,会找到最合适的不冲突版本,再到 pubspec.lock中找到版本号替换,最终不要直接用any,是个风险。

    #name很重要,如果修改了name所有的dart的文件的import前引用的本地的文件啊的包名都需要修改
    name: flutterdemo
    description: A new Flutter application.
     
    dependencies:
      flutter:
        sdk: flutter
     
     #添加依赖packages 
      cupertino_icons: ^0.1.2
      english_words: ^3.1.0
     # image_picker: ^0.4.8
     
    dev_dependencies:
      flutter_test:
        sdk: flutter
     
      #启用国际化
      flutter_localizations:
        sdk: flutter
     
    flutter:
     
      # The following line ensures that the Material Icons font is
      # included with your application, so that you can use the icons in
      # the material Icons class.
      uses-material-design: true
      # To add assets to your application, add an assets section, like this:
      #添加资源,不单单是图片,images是个和pubspec.yaml配置文件同级的目录,如果不同级,需要添加..
      assets:
            - images/park.jpg
            - images/lake.jpg
            - images/touxiang.jpg
      #  - images/a_dot_burr.jpeg
      #  - images/a_dot_ham.jpeg
       #字体设置
       fonts:
         - family: Schyler
           fonts:
           - asset: fonts/Schyler-Regular.ttf
            - asset: fonts/Schyler-Italic.ttf
               style: italic
         - family: Trajan Pro
           fonts:
          - asset: fonts/TrajanPro.ttf
          - asset: fonts/TrajanPro_Bold.ttf
            weight: 700
    
    2、关于MaterialApp和Scaffold:

    Flutter提供了两套不同风格的UI控件,分别是类Android风格的MaterialApp和类IOS风格的CupertinoApp。两种风格下面的widget不能完全通用,且Material风格的widget数量要多一些。

    ~ MaterialApp是Flutter提供给Android的一个基础widget,采用了材料设计风格。
    经过实践,MaterialApp全局最好只有一个,作为主界面,app的主题、主页等全局设置可以在此定义。
    最初按照官网教程每个page我都返回的MaterialApp,显示是没什么问题,因为都是widget,但是会出现卡顿和其他界面上的问题,大家可以自己试一下。

    ~ 子页面直接返回Scaffold,Scaffold是MaterialApp的布局实现,提供了appbar,floatingActionButton,drawer,bottomNavigationBar等MD风格的控件api。

    Flutter默认会在debug模式下在右上角显示水印,去除方式

    debugShowCheckedModeBanner: false
    
    3、自定义控件。

    Flutter框架给我们提供了StatelessWidget和StatefulWidget两个抽象类,用于自定义控件。
    (1)StatelessWidget是‘‘无状态控件’’,不可变状态控件,通过构建其他控件来描述用户界面的一部分。必须实现build方法,返回一个widget对象。 Icon、 IconButton, 和Text等都是无状态widget, 他们都是 StatelessWidget的子类。
    (2)StatefulWidget 是动态的. 用户可以和其交互 (例如输入一个表单、 或者移动一个slider滑块),或者可以随时间改变 (也许是数据改变导致的UI更新).Checkbox, Radio, Slider, Form, 和TextField 都是 stateful widgets, 他们都是 StatefulWidget的子类。

    (3)自定义Widget:继承StatefulWidget,并重写createState()方法,返回一个State对象。
    自定义无状态的widget:

    class RedBoard extends StatelessWidget {
      const RedBoard({ Key key }) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return new Container(
          color: Colors.red
        );
      }
    }
    

    自定义可变状态的widget:

    class RandomWords extends StatefulWidget {
      

    (4)继承自CustomPaint画控件
    Flutter也可以像Android中继承View的方式来绘制控件,通过继承CustomPaint类来实现,具体用法此处略。

    4、TextField样式
    decoration: new InputDecoration(
                      hintText: 'input name to search',
                      border: InputBorder.none
                  )
    

    去掉下边框。
    外面套上Container,加上装饰实现四面边框效果

    decoration: BoxDecoration(
                    color: Colors.white,
                    border: Border.all(color: Colors.green, width: 1.0),
                    borderRadius: BorderRadius.circular(4)),
    

    注意:
    在decoration中加了color属性,在Container中就不能加color属性,否则会出错。

    TextField坑
    软键盘resize窗口,解决方式:

    In your `Scaffold`, set `resizeToAvoidBottomPadding` property to `false`.
    

    preprefixIcon和suffixIcon如果使用系统提供的svg资源,需要指定颜色,不然在获取焦点时会变成不可见状态。

    5、ListView

    ListView的使用可以参考此文:https://blog.csdn.net/hao_m582/article/details/84112278

    如果与其他widget放在同一个Column中,ListView外加Expanded才能正常显示。

    可以用ListView作为滚动块,相当于android中的ScrollView效果,但是子view不是写在widget中,而是直接写在ListView的children属性中,如:

    //...
    body: new ListView(
      children: [
        new Image.asset(
          'images/lake.jpg',
          width: 600.0,
          height: 240.0,
          fit: BoxFit.cover,
        ),
        titleSection,
        buttonSection,
        textSection,
      ],
    ),
    //..
    
    6、加载图片与控件缩放

    需要在pubspec.xml中配置图片路径,可以看上段代码片段。

      assets:
            - images/park.jpg
            - images/lake.jpg
            - images/touxiang.jpg
    

    其中images文件夹放在工程的根目录。加载图片可以直接使用AssertImage类,也可以使用Image.asset方法。
    经过查找,flutter不完全支持svg,xml格式的VectorDrawable在flutter上无法直接加载
    类似Android中ImageView的scaleType属性,flutter的Image控件也有其属性Boxfit,而且这个属性不仅仅适用于Image相关的Widget,FittedBox也具有此属性。
    FittedBox会在自己的尺寸范围内缩放并且调整child位置,使得child适合其尺寸。
    示例代码:

    new Container(
      color: Colors.amberAccent,
      width: 300.0,
      height: 300.0,
      child: new FittedBox(
        fit: BoxFit.contain,
        alignment: Alignment.topLeft,
        child: new Container(
          color: Colors.red,
          child: new Text("FittedBox"),
        ),
      ),
    )
    

    看一下几种缩放方式的区别:


     
    image.png
    7、Flutter构建布局实例

    Flutter布局机制的核心是Widget。在Flutter中,几乎所有东西都是Widget - 甚至布局模型都是Widget。你在Flutter应用中看到的图像、图标和文本都是widget。 甚至你看不到的东西也是widget,例如行(row)、列(column)以及用来排列、约束和对齐这些可见widget的网格(grid)。

    查看Flutter中文网的教程:在Flutter中构建布局

    常用Widget
    (1)Column和Row相对于Android中的LinearLayout,Column相对于Orientation.Vertical;Row相当于Orientation.Horizontal。

    (2)ListView ,GridView与Android中的同名控件效果等同

    ListTitle是Flutter封装好的在列表中显示的item控件,他有固定的显示格式,如下:
     
    ListTitle.jpg

    (3)Stack相当于Android中的FrameLayout,但是它又具有RelativeLayout的一些属性。

    (4)Card相当于Android中的CardView

    (5)事件响应:Flutter并非为所有Widget都直接提供了点击,长按等事件响应,这时我们需要用 GestureDetector这个widget包裹需要响应事件的widget来实现功能。
    GestureDetector提供了如下手势:
      Tap
      onTapDown 指针已经在特定位置与屏幕接触
      onTapUp 指针停止在特定位置与屏幕接触
      onTap tap事件触发
      onTapCancel 先前指针触发的onTapDown不会在触发tap事件
      双击
      onDoubleTap 用户快速连续两次在同一位置轻敲屏幕.
      长按
      onLongPress 指针在相同位置长时间保持与屏幕接触
      垂直拖动
      onVerticalDragStart 指针已经与屏幕接触并可能开始垂直移动
      onVerticalDragUpdate 指针与屏幕接触并已沿垂直方向移动.
      onVerticalDragEnd 先前与屏幕接触并垂直移动的指针不再与屏幕接触,并且在停止接触屏幕时以特定速度移动
      水平拖动
      onHorizontalDragStart 指针已经接触到屏幕并可能开始水平移动
      onHorizontalDragUpdate 指针与屏幕接触并已沿水平方向移动
      onHorizontalDragEnd 先前与屏幕接触并水平移动的指针不再与屏幕接触,并在停止接触屏幕时以特定速度移动。

    (6)Button:Flutter提供了几种样式的按钮,分别为:
      FlatButton:扁平的,没有阴影效果的。
      RaisedButton:有阴影效果的。
      FloatingActionButton:悬浮按钮,类似Android上同名的控件。
      OutlineButton:线框按钮,带外边框的按钮。

    (7)Expanded、Flexible:Expanded 这是个用来让子项具有伸缩能力的widget,继承自Flexible。它们两个的默认灵活系数是一样的,但是fit参数不同,Expanded是默认要占满分配的空间的,而Flexible则默认不需要。

    (8)Ripple效果:Flutter中文网将InkWell翻译成“墨水飞溅”效果,其实看到效果后,我们马上就能联想到Android中的ripple效果。


     
    inkwell.gif

    用法也是用InkWell套起想要达到效果的widget,具体属性查看源码注释。

    下面用一个具体的例子介绍布局和其他可能用到的Widget:

     
    聊天.png

    上图是常见的聊天列表样式,首先我们能想到的是整个界面是一个ListView,根据类型有左边和右边两种样式。由于flutter没有xml布局,所有界面都是通过widget搭积木一样,一层一层套起来的。

    左侧的显示:最外层应该是一个Row,Row中包含了一个CircleAvatar(没错,这个Widget官方直接提供了)和一个Text。
    怎样控制Text的背景样式:
    首先想到的就是外层套一个可以设置样式的Container,通过给Container加一个decoration属性,一般使用BoxDecoration,可以为Container设置背景颜色,前景颜色,边框,圆角,图片等能满足大部分场景的样式。
    问题出现了

     
    image.jpg

    Text本身是支持文字自动换行的,Container本身如果没有父控件限制也是包裹的,但因为外面放了一个Row,就会出现溢出屏幕的问题。经过多番查找,我找到了一个Widget可以解决问题——Flexible,在Container外面套一个Flexible就能解决问题,此时需要注意的是,Flexible,Expanded等可以自适应的继承了Flex的控件,其父控件也必须是同类型。

    接下来我们要控制文字的最长显示宽度,Container有一个属性是constraints,它的类型是BoxConstraints,这个Widget可以设置最小最大宽高,不限制的话就用double.infinity(无限)。在经过限制后,我们发现Flexible已经不需要了,因为宽度已经限制住了=.=|||。

    //获取屏幕宽度的方法
    double width = MediaQuery.of(context).size.width;
    

    接下来我们按照Android中的理解,显示右侧头像的消息,就在Row中先加入一个Text,再加入一个CircleAvatar。没错,但是怎样居右显示呢?经过查询资料发现,Row通过textDirection属性可以设置方向,我们将属性设置成TextDirection.rtl,也就是rightToLeft,发现咦?怎么头像跑到前面去了?那我们再把头像代码移到前面,竟然对了。。也就说明,Row的绘制流程都是根据children中最先加入的子widget来绘制的。

    输入框实现:
    输入框首先要保持在界面底部,怎么实现呢?了解到官方提供了一个BottomAppBar,将其设置给Scaffold中的bottomNavigationBar属性。BottomAppBar的child给到一个Row控件,排列语音按钮IconButton,输入框TextField,更多按钮IconButton。TextField外部要嵌套一个Container修饰样式。运行后发现整个界面都无法显示,而注释掉TextField就可以显示,由此想到应该是TextField的宽度不正常导致的,使用万能控件Flexible套在TextField的父级Container外后显示正常。
    接下来试试输入文字,又出现坑了!BottomAppBar在输入法弹出时无法自动上移,确定了resizeToAvoidBottomPadding设置为true的情况依然无法解决问题后,只好找其他方式。在stackoverflow上找到了另一种方案:将最下面的输入布局连同ListView都放入Scaffold的body中,ListView外加上Expanded伸缩,最外层一个Container包裹,运行完美,代码如下:

     @override
      Widget build(BuildContext context) {
        _getConversations();
        return new Scaffold(
          appBar: new AppBar(
            title: new Text(mIsGroup
                ? mConversation.groupBean.groupName
                : mConversation.contactBean.nickName != null
                    ? mConversation.contactBean.nickName
                    : mConversation.contactBean.pin),
            elevation: 0.5,
            actions: <Widget>[
              new IconButton(
                  icon: new Icon(
                    mIsGroup ? Icons.group : Icons.person,
                    size: 24,
                    color: Colors.black54,
                  ),
                  onPressed: _goContactInfo),
            ],
          ),
          body: new Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              new Expanded(
                child: _buildConversations(),
              ),
              Row(
                mainAxisSize: MainAxisSize.max,
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: <Widget>[
                  IconButton(icon: Icon(Icons.keyboard_voice), onPressed: null),
                  Flexible(
                    child: Container(
                        height: 40,
                        margin: EdgeInsets.fromLTRB(10, 6, 10, 6),
                        padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
                        decoration: BoxDecoration(
                          color: Colors.white,
                          border: Border.all(
                            color: Colors.black12,
                            width: 0.5,
                            style: BorderStyle.solid,
                          ),
                          borderRadius: BorderRadius.circular(4),
                        ),
                        child: TextField(
                          decoration: InputDecoration(
                              hintText: '输入内容', border: InputBorder.none),
                        )),
                  ),
                  Container(
                    margin: EdgeInsets.fromLTRB(0, 0, 10, 0),
                    child: IconButton(icon: Icon(Icons.add_circle_outline),onPressed: null,),
                  )
                ],
              ),
            ],
          ),
        );
      }
    

    到此,就实现了图中所示效果。
    下面是item的布局代码:

    Widget _msgHolder(MessageBean message, BuildContext context) {
        double width = MediaQuery.of(context).size.width;
        double _maxWidth = width * 0.65;
        return Row(
          textDirection: message.from.pin == myInfo.pin?TextDirection.rtl:TextDirection.ltr,
          children: <Widget>[
            CircleAvatar(
              backgroundImage: AssetImage(message.from.avatarUrl == null
                  ? "assets/drawable/ava_group.png"
                  : message.from.avatarUrl),
            ),
            GestureDetector(
                onLongPress: () {
                  _showList(options);
                },
                child: Container(
                  constraints: BoxConstraints(
                      minWidth: 0,
                      maxWidth: _maxWidth,
                      minHeight: 0,
                      maxHeight: double.infinity),
                  margin: EdgeInsets.fromLTRB(10, 5, 5, 10),
                  padding: EdgeInsets.all(8),
                  decoration: BoxDecoration(
                    color: Colors.green,
                    border: Border.all(
                      color: Colors.green,
                      width: 0.5,
                      style: BorderStyle.solid,
                    ),
                    borderRadius: BorderRadius.circular(4),
                  ),
                  child: Text(
                    _getMessage(message),
                    style: TextStyle(color: Colors.white, fontSize: 16),
                  ),
                )),
          ],
        );
      }
    

    Widget的显示与隐藏
    Flutter知识点: Widget隐藏与显示

    8、Toast和Dialog

    在聊天中,常见操作是长按消息,弹出一个具有选项的Dialog,那么在Flutter中如何显示常见的Toast和Dialog呢?
    由于Flutter提供给安卓的大部分Widget都是基于Material设计的,所以Flutter并没有提供Toast控件,而是提供了SnackBar

    //SnackBar的显示
    Scaffold.of(context).showSnackBar(SnackBar(
         content: Text("长按消息"),
    ));
    

    SnackBar不止能够显示Text,还可以任意加入widget。
    此处有坑:
    关于上段代码中的context(BuildContext),你在任何方法里都可以取到context,但是运行起来很可能会遇到context为null的情况,那么就需要传入一个经过了实例化的BuildContext。比如Scaffold中或者ListView.builder中的context,传给SnackBar即可。

    Dialog:
    Flutter为Android提供了Dialog,AlertDialog,SimpleDialog三种常用对话框。想要实现我们的需求用SimpleDialog刚合适,效果如下:

     
    弹窗.jpg
    showDialog<int>(
            context: context,
            builder: (BuildContext context) {
              return new SimpleDialog(
                children: options.map((value) {
                  return new SimpleDialogOption(
                    onPressed: () {
                      Navigator.pop(
                          context,
                          options.indexOf(
                              value)); //here passing the index to be return on item selection
                    },
                    child: new Text(
                      value,
                      style: TextStyle(
                        fontSize: 16,
                      ),
                    ), //item value
                  );
                }).toList(),
              );
            })
    
    9、数据存储

    Flutter知识点:数据存储之SharedPreferences
    Flutter知识点:数据存储之File
    Flutter知识点:数据存储之sqflite
    官网sqflite页面
    下面的字段不能用于表的属性名称

    "add","all","alter","and","as","autoincrement","between","case","check","collate","commit","constraint","create","default","deferrable","delete","distinct","drop","else","escape","except","exists","foreign","from","group","having","if","in","index","insert","intersect","into","is","isnull","join","limit","not","notnull","null","on","or","order","primary","references","select","set","table","then","to","transaction","union","unique","update","using","values","when","where"
    
    
    10、Flutter常用插件
      audio_recorder: any #录音、播放
      flutter_sound: ^1.1.5#录音
      dropdown_menu: ^1.1.0#下拉菜单
      simple_permissions:#权限获取
      easy_alert:#弹框
      amap_location: any #高德地图
      location: any #gogle位置获取
      barcode_scan 0.0.8#二维码识别qr_mobile_vision: ^0.1.0 #二维码识别 这个不好用
      flutter_screenutil: ^0.3.0#屏幕适配工具类  
      flutter_spinkit: ^2.1.0#加载等待框
      lpinyin: ^1.0.6#汉字转拼音
      shimmer: ^0.0.4#微光效果控件
      qr_flutter: ^1.1.3#二维码生成
      url_launcher: any#启动URL的Flutter插件。支持网络,电话,短信和电子邮件
      datetime_picker_formfield: ^0.1.3#时间选择控件
      flutter_picker: '^1.0.0'#选择器
      common_utils: '^1.0.1'#工具类 时间、日期、日志等
      flutter_html: '^0.8.2'#静态html标记呈现为Flutter小部件
      fluwx: '^0.3.0'#微信支付、分享、登录
      tobias: '^ 0.0.6#支付宝
      cupertino_icons: '^0.1.2'#小图标控件
      http: '^0.11.3+16'#网络请求
      html: '^0.13.3'#html解析
      image_picker: '^0.4.5'#图片选择(相册或拍照)
      flutter_webview_plugin: any#webview展示
      fluttertoast: any#toast提示框
      shared_preferences: '^0.4.2'#shared_preferences存储
      transparent_image: '^0.1.0'#透明图片控件
      flutter_swiper : '^1.0.2'#轮播图
      charts_flutter: '^0.4.0'#统计图表
      path_provider: '^0.4.1'#获取系统文件
      cached_network_image: '0.4.1'#加载网络图片并本地缓存
      sqflite: '^0.11.0+1'#sqllite数据库操作
      pull_to_refresh: '^1.1.5'#下拉刷新上拉加载更多
      video_player: '0.6.1'#视频播放
      collection: '1.14.11'#集合操作工具类
      device_info: '0.2.1'#获取手机信息
      flutter_svg: '^0.3.2'#展示svg图标控件
      intl: any#国际化工具类
      connectivity: '0.3.1'#链接网络
      flutter_staggered_grid_view:#瀑布流展示控件
      flutter_file_manager:#文件管理
      loader_search_bar:#导航栏搜索控件
      flutter_image_compress : any#图片压缩
      ota_update : any#App下载更新
      flutter_slidable:#item侧滑控件
    
  • 相关阅读:
    Adobe Flash Builder 4.5 Android Air 程序开发系列 之六 多点触控
    Adobe Flash Builder 4.5 Android Air 程序开发系列 之九 定位
    Adobe Flash Builder 4.5 Android Air 程序开发系列 之七 重力感应
    Adobe Flash Builder 4.5 Android Air 程序开发系列 之五 保存数据的几种方式
    Adobe Flash Builder 4.5 Android Air 程序开发系列 之八 照相机
    Adobe Flash Builder 4.5 Android Air 程序开发系列 之三 Application 配置详解
    Adobe Flash Builder 4.5 Android Air 程序开发系列 之四 打开与关闭应用程序是的保存数据
    ADOBE FLASH BUILDER 4.6 IOS 开发之部署与调试
    [译] 高性能JavaScript 1至5章总结
    页签及盒子的web标准实现
  • 原文地址:https://www.cnblogs.com/sea520/p/12044297.html
Copyright © 2011-2022 走看看