Flutter(1)-初探Flutter,感受Dart

RN想必大家都知道,它的技术底层是沿用了native的技术,渲染就是用原始的渲染的,所以会导致安卓和iOS的兼容性不太好,组件会分平台,不好维护,连忠实的追随者Airbnb也不得不放弃了,改为最初的原生开发了。
Flutter,2015年10月就有了,2018年12月正式版本1.0发布,是谷歌开发的,渲染引擎是自己用C++搞出来的,不再借用原生的。个别地方iOS和安卓的处理方式不一样,但是这个不一样在开发者这层是体现不出来的,代码只需要一套,所以在兼容性上就非常的好。
本篇文章开始慢慢的探究一下Flutter。环境的配置大家去Flutter官网Flutter中文网探索安装就可以了。

1.初次接触

本篇开始带着大家一起慢慢的学习Flutter,开发工具使用的是Android Studio,当然你用VS Code也可以。cd进入到对应的文件夹然后命令行flutter create boxdemo创建一个boxdemo的flutter工程,flutter的命令这里也不再赘述,良心话,真正开发的过程中像这种创建工程、运行工程的有可视化的如果还用命令行那就是纯属装逼,哈哈。Android Studio的flutter插件安装过了的话直接就直接可以在界面上点击New Flutter Project,按照步骤一步一步即可创建flutter工程,创建完后如果发现无法在iOS模拟器或者真机上运行,请用xcode打开ios目录下的Runner.xcworkspace进行证书设置,然后就可以正常运行了。


优先来一个Flutter Application工程。用Android Studio运行的过程中如果出现意外中断后,再也运行不起来或者运行的不是最新代码,请在终端执行rm ~/flutter/bin/cache/lockfile删除flutter/bin里面的缓存区就可以了,flutter的路径请按照自己电脑上的路径我这里就是放在~中。Flutter主要的语言是dart,在后面的写代码过程中我们会一直用到它,新建完工程后,我们从main.dart入手,这里是flutter工程的起始点,就像是一个iOS工程的AppDelegate一样,会发现main.dart有一个引入 import 'package:flutter/material.dart';,这个引入在以后的日子你会经常看到,类似于iOS中的UIKit,是谷歌给大家提供的一套UI方案。看着初始工程的main.dart里面代码有点多,全部干掉,来一个简单的代码:

import 'package:flutter/material.dart';

void main() {
  runApp(
      Center(
        child: Text('BoxJing')
      )
  );
}

这代码足够简单了,就是一个简单的在中间位置显示一个BoxJing字符串。
在Flutter工程中会有一个Widget(小部件)我们会频率很高的见到它,这个东西就是我们在iOS里面的控件。Widget分为下面两种:

  • stateless: 无状态,死板板的显示,初始化后无法再改变
  • stateful: 有状态,可以根据需要刷新

那么很明显,我们可以根据自己的需要来封装不同的Widget,那么先来个我们自己封装一个简单的statelessWidget:

import 'package:flutter/material.dart';

void main() {
  runApp(
      BoxWidgetLess()
  );
}

class BoxWidgetLess extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Center(
        child: Text('一个阿狸', textDirection: TextDirection.ltr)
    );
  }
}

如果某个函数里只有一句代码的时候,比如上面main函数,我们可以直接简化为:void main() => runApp(BoxWidgetLess());。既然我们用到了Text这个Widget,下面就仔细的说一下这个Text:

class Text extends StatelessWidget {
  /// The [data] parameter must not be null.
  const Text(
    this.data, {
    Key key,
    this.style,
    this.strutStyle,
    this.textAlign,
    this.textDirection,
    this.locale,
    this.softWrap,
    this.overflow,
    this.textScaleFactor,
    this.maxLines,
    this.semanticsLabel,
    this.textWidthBasis,
  }) : assert(
         data != null,
         'A non-null String must be provided to a Text widget.',
       ),
       textSpan = null,
       super(key: key)

  final TextStyle style;
  ...
  final TextAlign textAlign;

也就是说第一个参数data必须传值,后面大括号里面的全是可选参数,来个实实在在的例子,文字带大小和颜色最基本的操作:

class BoxWidgetLess extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    final _boxTxtStyle = TextStyle(
      color: Colors.blue,
      fontSize: 36.0,
      fontWeight: FontWeight.bold
    );
    return Center(
        child: Text('一个阿狸',
            textDirection: TextDirection.ltr,
            style: _boxTxtStyle
        )
    );
  }
}

还是通俗易懂的,多练习练习就熟练了。下面来个一直想要的界面效果:

void main() => runApp(BoxApp());
class BoxApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
//      debugShowCheckedModeBanner: false, //隐藏右上角DEBUG标识
      home: Scaffold(
        appBar: AppBar(
          title: Text('Box Demo'),
        ),
        body: BoxWidgetLess(),
      ),
      theme: ThemeData(
        primaryColor: Colors.purple
      ),
    );
  }
}

前后对比效果来一个:


这个MaterialApp其实就是系统帮我们封装了一个可以让我们快速开发的手脚架,点进去源码,解释的也很清楚,Scaffold就像是iOS开发中的UINavigationController,实际上比它要弱,可以暂时的这样理解起来,appBar就是导航条,body就是导航条下面的空白部分,theme可以用来设置一些主题颜色,其他很多小东西大家就自己试一试。

2. ListView

我们在开发中用到的最多的ListView,那必须来一波操作,先来个简单的代码:

void main() => runApp(BoxHome());
class BoxApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: BoxHome(),
      theme: ThemeData(
          primaryColor: Colors.blue
      ),
    );
  }
}
class BoxHome extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Box Demo'),
      ),
      body: ListView.builder(
          itemCount: 10,
          itemBuilder: _boxCellForRow,
      ),
    );
  }
// cellForRow方法
  Widget _boxCellForRow(BuildContext context, int index) {
    return Text('阿狸');
  }
}


效果就是返回10行文字,全是阿狸。从代码中可以看到ListView的构建方法使用ListView.builder,里面的参数也是非常好理解的,itemCount就是行数,itemBuilder就是每个cell,其他的参数直接去看源码注释,Flutter中的ListView是没有iOS中TableViewSection的概念的。简单的把cell的构建摘出去一个单独方法来操作了,这里说一点,在Dart中带_的属于内部的变量或者方法,外部不可访问,不带_属于公开的。这里我们为了达到一个更好的效果,新建一个people.dart文件来作为一个model,等下我们会用这个model来填充cell:

class People {
// 构造方法
  const People({
    this.name,
    this.avatar
  });
// 两个属性  final 不可变的  var 可以更改的
  final String name;
  final String avatar;
}
// 这里声明一个公开的数据源 后面外部需要使用填充cell
final List<People> boxArr = [
  People(name:'阿狸1',avatar: 'http://cache.fotoplace.cc/ap2/190521/0/7B5C54B95100DE281E689EEF24E499D9.png'),
  People(name:'阿狸2',avatar: 'http://cache.fotoplace.cc/ap2/190521/0/6478737E09CA39170389191133953111.png'),
  People(name:'阿狸3',avatar: 'http://cache.fotoplace.cc/ap2/190521/0/5FD190ECFF4943167F3D5F19A2973D6D.png')
];

然后在main.dart中引入import 'model/people.dart';并且改掉cellForRow方法:

// 直接一个图片控件
Widget _boxCellForRow(BuildContext context, int index) {
    return Container(
      color: Colors.white,
      height: 88.0,
      margin: EdgeInsets.all(6.0),
      child: Image.network(boxArr[index].avatar),
    );
  }

这里就是放一个单独的控件Image来加载网络图片,在开发中很少用到,一般情况下我们都是多个控件在一个cell中,来几个多控件其他布局,可以放开不同的注释看效果,完整的代码可以在文章底部的github上下载运行:

// cellForRow方法
////  横向 Row
//  Widget _boxCellForRow(BuildContext context, int index) {
//    return Container(
//      color: Colors.white,
//      height: 88.0,
//      margin: EdgeInsets.all(6.0),
//      child: Row(
//        children: <Widget>[
//          Image.network(boxArr[index].avatar),
//          Text(boxArr[index].name)
//        ],
//      ),
//    );
//  }
////  竖向 Column
//  Widget _boxCellForRow(BuildContext context, int index) {
//    return Container(
//      color: Colors.white,
//      height: 240.0,
//      margin: EdgeInsets.all(6.0),
//      child: Column(
//        children: <Widget>[
//          Image.network(boxArr[index].avatar),
//          Text(boxArr[index].name)
//        ],
//      ),
//    );
//  }
////  垂直 z轴 Stack
//  Widget _boxCellForRow(BuildContext context, int index) {
//    return Container(
//      color: Colors.white,
//      height: 240.0,
//      margin: EdgeInsets.all(6.0),
//      child: Stack(
//        children: <Widget>[
//          Image.network(boxArr[index].avatar),
//          Text(boxArr[index].name)
//        ],
//      ),
//    );
//  }

先来看一下这三种效果:


这三种布局会是我们在整个Flutter开发中使用到,各种各样的布局都是用这三种布局来实现的。一般情况下,我们自定义cell最外层会用一个Container来包一层,顾名思义是一个容器,其实就是一个很干净的部件,在单个控件中我们直接是在child里面放置了一个控件就可以了,多个的时候,一定要有一个布局的方式,所以使用这三个中的其中一个包多个,用了children,这命名相当的人性化吧。这时候,如果想文字和图片中间有点间距怎么办?使用SizedBox来进行间隔设置,就是图片和文字之间放了一个宽度为10pt的空白区域,我代码里使用的是SizedBox(width: 10),如果是竖排方向间隔那就是SizedBox(height: 10),直接上代码:

//  横向 Row
  Widget _boxCellForRow(BuildContext context, int index) {
    return Container(
      color: Colors.white,
      height: 88.0,
      margin: EdgeInsets.all(6.0),
      child: Row(
        children: <Widget>[
          Image.network(boxArr[index].avatar),
          SizedBox(width: 10),
          Text(boxArr[index].name)
        ],
      ),
    );
  }

margin这个东西如果你接触过CSS,应该就很明白是干嘛用的,就是用来设置边距的,再看后面的赋值EdgeInsets,作为一个iOS开发者很熟悉,经常会用到的设置上下左右的距离,在Flutter中常用的有allonly,all就是直接设置四边距离都是同一个值,only的话就需要指明是哪个方向的边距是多少,还有其他的symmetricfromWindowPadding等方法看看注释,试一试就可以了,看一下运行效果:


如果想固定这个图片的大小,那就直接给Image设置widthheight来实现,可以直接去掉Container的height了:

//  横向 Row
  Widget _boxCellForRow(BuildContext context, int index) {
    return Container(
      color: Colors.white,
//      height: 88.0,
      margin: EdgeInsets.all(6.0),
      child: Row(
        children: <Widget>[
          Image.network(boxArr[index].avatar,width: 60.0,height: 60.0),
          SizedBox(width: 10),
          Text(boxArr[index].name)
        ],
      ),
    );
  }

看一下运行效果:


如期达到了我们想要的效果。有一点要注意,并不是所有的部件都有widthheight属性的,如果没有,就把它放进一个ContainerContainer是有widthheight属性的。

本篇就简单的接触一下Flutter,感受一下Dart语言,所有的代码都可以在Github:BoxJ/Flutter-daydayup中下载,本篇代码的文件夹是boxdemo_001,欢迎一起交流!

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

推荐阅读更多精彩内容