Flutter从入门到寄几玩儿

国庆后面两天在家学习整理了一波flutter,基本把能撸过能看到的代码都过了一遍, 此文篇幅较长,建议保存(star)再看。传送门: Nealyang personal blog

前言

毕竟前端出生,找(qi)到(shi)了(bing)感(mei)觉(ru)后(men),其实就是一个UI框架,只不过他的引擎基于C++,底层基于Skia渲染,DartVM虚拟机以及Text and so on...css

2018年6月21日Google发布Flutter首个release预览版,做为Google baba大力推出的一种全新的响应式,跨平台,高性能的移动开发框架,势必会火一波~没别的,就是由于Google baba,固然,从目前看来也的确愈来愈火了。
[Questions tagged [flutter]](https://stackoverflow.com/que...
imghtml

本文咱们从介绍flutter基本概念到梳理经常使用Widget到经常使用app demos编写到~放弃~,但愿能够帮助每个像我同样的初学者。有误地方还望大神不吝赐教~前端

img

国际惯例,吹一波先~react

直接移步Flutter官宣pptandroid

关于Dart

做为Flutter入门文章,Dart必然少不了,固然,做为Flutter入门篇,Dart预发基础必然不会过多介绍。git

Dart入门传送门:Dart or Dart2,或者从Dart中文网中学习也不错其实.github

这里咱们说说为啥是Dart。web

许多语言科学家认为,一我的说的天然语言会影响他们的思惟方式。早起Flutter团队评估了十多种语言最终选择了Dart,由于它符合他们构建用户界面的方式编程

  • Dart 是AOT 编译的,编译成快速可预测的本地代码,使Flutter几乎均可以使用Dart编写,这不只使Flutter变的更快,并且几乎全部的东西均可以定制
  • Dart也能够JIT编译,开发周期异常快,工做流颠覆常规,也使得Flutter能够实现很是Diao的有状态热重载(别扯别的,人家是出生自带哇)
  • Dart能够更轻松地建立以60fps运行的流畅动画和转场。Dart能够在没有锁的状况下进行对象分配和垃圾回收。就像JavaScript同样,Dart避免了抢占式调度和共享内存(于是也不须要锁)。因为Flutter应用程序被编译为本地代码,所以它们不须要在领域之间创建缓慢的桥梁(例如,JavaScript到本地代码)。它的启动速度也快得多
  • Dart使Flutter不须要单独的声明式布局语言,如JSX或XML,或单独的可视化界面构建器,由于Dart的声明式编程布局易于阅读和可视化。全部的布局使用一种语言,汇集在一处,Flutter很容易提供高级工具,使布局更简单
  • Dart对于IOS、Android、Web FE来讲,都还比较友好。

具体选择Dart的缘由,以及向了解Dart的,移步为何Flutter会选择 Dart浏览器

关于Flutter

刚开始接触flutter心中不免会有疑惑,不是已经有RN、Weex等各类跨平台移动开发 了,flutter优点在哪呢? 看我从网上盗的图!

img

图片来源简书

Everything is Widget

有一种说法认为函数式语言和命令式语言的不一样在于命令式语言是给计算机下达指令而函数式语言是向计算机描述逻辑。这种思路在Flutter UI中获得了体现。Flutter不提倡去操做UI,它固然也基本不会提供操做View的API,好比咱们常见的相似TextView.setText(),Button.setOnClick()这种是不会有的。对界面的描述是能够数据化的(相似XML,JSON等),而对界面的操做是很难数据化的,这很重要,响应式须要方即可持续的将数据映射成界面。

在Flutter中用Widget来描述界面,Widget只是View的“配置信息”,编写的时候利用Dart语言一些声明式特性来获得相似结构化标记语言的可读性。Widget根据布局造成一个层次结构。每一个widget嵌入其中,并继承其父项的属性。没有单独的“应用程序”对象,相反,根widget扮演着这个角色。在Flutter中,一切皆为Widget,甚至包括css样式。

<div class="greybox">
    Lorem ipsum
</div>

.greybox {
      background-color: #e0e0e0; /* grey 300 */
      width: 320px;
      height: 240px;
      font: 900 24px Georgia;
    }

在flutter中咱们编写为

var container = new Container( // grey box
  child: new Text(
    "Lorem ipsum",
    style: new TextStyle(
      fontSize: 24.0
      fontWeight: FontWeight.w900,
      fontFamily: "Georgia",
    ),
  ),
  width: 320.0,
  height: 240.0,
  color: Colors.grey[300],
);

能够看到咱们css样式中的font定义的样式,在flutter中,须要new TextStyleTextStyle就是一个Widget,而且样式必须做用与Container中的child:text上,不存在web中样式的继承。

刚开始接触的同窗就类比于react中扯的,一切皆为组件吧,其实widget是对页面UI的一种描述。他功能类有点似于android中的xml,react中的jsx。widget在渲染的时候会转化成element。Element相比于widget增长了上下文的信息。element是对应widget,在渲染树的实例化节点。因为widget是immutable的,因此同一个widget能够同时描述多个渲染树中的节点。可是Element是描述固定在渲染书中的某一个特定位置的点。简单点说widget做为一种描述是能够复用的,可是element却跟须要绘制的节点一一对应。那element是最终渲染的view么?抱歉,还不是。element绘制时会转化成rendObject。RendObject才是真正通过layout和paint并绘制在屏幕上的对象。在flutter中有三套渲染相关的tree,分别是:widget tree, element tree & rendObject tree。三者的渲染流程以下:

img

有没有一种 jsx -> virtual Dom -> real dom滴感受呢~
img

咳咳,后面会介绍基础经常使用的Widget配合一些demo,你们可能对这个体会就会更加清晰一些。

组合大于继承

Flutter中不少借鉴了react的思想,甚至包括后面会说到的state。

Widget自己一般由许多更小的、单一的小小widget组成,甚至小到它单一下来并无什么做用的感受,这些Widget几几组合造成一个强大的自定义的大大Widget。

好比一个Container,对于Web FE来讲可能就是个div,而他就是由不少的widget组成,这些widget负责布局、绘制、定位、大小等。咱们可使用各类姿式来组合他们而不是继承他们。类层次结构很浅且很宽,能够最大限度的增长可能组合的数量

img

框架结构

img
上面的图片是Flutter分层框架结构图,对大部分开发者而言,最经常使用的是Widgets层,屏幕上可见与不可见的元素都由Widgets层实现,这些元素被称为Widget。在Widgets层在上层,有两个现成的Widget库,Material库即Material Design的Widget库,Material Design是Google I/O 2014发布的设计语言,目前成为统一Android Mobile、Android Table、Desktop Chrome等平台的设计语言规范。Cupertino库则是一个模仿iOS设计风格的Widget库。

底层是Flutter Engine虚拟机,在这一层次中须要了解一下的是Skia,Skia是Google研发的包括图形、文本、图像、动画等多方面的图形引擎,不只用于Google Chrome浏览器,Android系统也采用Skia做为绘图处理引擎。

GPU渲染:
img

state生命周期:
img

做为初学者看上面的图有点云里雾里的,且先作到内心有数~

Flutter蜻蜓点水

关于Flutter环境问题这里再也不赘述
此后~大量代码来袭

基础Widget之material版Hello world

国际惯例,hello world

import 'package:flutter/material.dart';

class MyAppBar extends StatelessWidget{
  MyAppBar({this.title});//
  final Widget title;

  @override
  Widget build(BuildContext context){
    return new Container(
      height: 56.0,
      padding: const EdgeInsets.symmetric(horizontal:8.0),
      decoration: new BoxDecoration(
        color:Colors.blue[400]
      ),
      child: Row(
        children: <Widget>[
          new IconButton(
            icon:new Icon(Icons.menu),
            tooltip:'Navigation menu',
            onPressed: (){
              print('点击Menu');
            },
          ),
          new Expanded(
            child:new Center(
              child:title
            )
          ),
          new IconButton(
            icon:Icon(Icons.search),
            tooltip:'Search',
            onPressed: (){
              print('点击搜索按钮');
            },
          )
        ],
      ),
    );
  }
}

class MyScaffold extends StatelessWidget{
  @override 
  Widget build(BuildContext context){
    return Material(
      child: new Column(
        children:<Widget>[
          new MyAppBar(
            title:new Text(
              'Hello World',
              style:Theme.of(context).primaryTextTheme.title
             ),
          ),
          new Expanded(
            child:new Center(
              child:Text('Hello World!!!')
            )
          )
        ]
      ),
    );
  }
}

void main(){
  runApp(
    new MaterialApp(
      title:'My app',
      home:new MyScaffold()
    )
  );
}

img
代码地址:https://github.com/Nealyang/flutter

这个UI的确有些对不起人了,上面的title被挡住了。且先不去适配,后面咱们使用Material提供的Scaffold便可

第一个例子,重点说下代码(用过的Widget记住):

  • 一切都是Widget,且Widget前面的new无关紧要。
  • 类MyAppBar和MyScaffold中使用了Container、Row、Column、Text、IconButton、Icon、BoxDecoration、Center、Expanded等经常使用Widget

    • Container一个拥有绘制、定位、调整大小的 widget。相似于div,咱们能够用它来建立矩形视图,container 能够装饰为一个BoxDecoration, 如 background、一个边框、或者一个阴影。 Container 也能够具备边距(margins)、填充(padding)和应用于其大小的约束(constraints)。另外, Container可使用矩阵在三维空间中对其进行变换。
    • RowColumn其实就是flex布局中的flex-direction
    • Expanded它会填充还没有被其余子项占用的的剩余可用空间。Expanded能够拥有多个children。而后使用flex参数来肯定他们占用剩余空间的比例。更多细节能够参看:flutter控件Flexible和 Expanded的区别
  • 先定义了一个MyAppBar的类,构造函数中接受一个Widget的title,其实咱们也能够接受String title而后在类中本身去new Title(title)
  • runApp函数接受给定的Widget并使用其做为widget根。
  • widget的主要工做是实现一个build函数,用以构建自身。一个widget一般由一些较低级别widget组成。Flutter框架将依次构建这些widget,直到构建到最底层的子widget时,这些最低层的widget一般为RenderObject,它会计算并描述widget的几何形状。

基本交互之material版Hello world

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  // app的根Widget
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        // 这是设置的app主题
        // 运行后你能够看到app有一个蓝色的toobar,而且在不退出app的状况下修改代码会热更新
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

// 这是应用中一个基类,继承自StateFulWidget,意味着这个类拥有一个state对象,该对象里的一些字段会影响app的UI
// 这个类是state的一些配置项。经过构造函数来获取值,这个值通常在State中消费,而且使用final关键字。其实相似于react中的defaultProps

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      // setState方法告诉Flutter,这个State中有些值发生了变化,以便及时将新值更新到UI上,
      // 若是我不经过setState更改_count字段,那么Flutter并不会调用build匿名函数去更新界面
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // build方法会在每次setState的时候从新运行,例如上面的_incrementCounter方法被调用
    //Flutter已经被优化了从新构建的方法,因此你只会去更新须要去更新的部分,没必要去单独更新里面的一些更细小的widget,相似于React中diff
    return new Scaffold(
      appBar: new AppBar(
        // 这里咱们使用从App.build方法中初始化MyHomePage时候传入的title值来设置咱们的title
        title: new Text(widget.title),
      ),
      body: new Center(
        // Center是一个布局Widget,他只有一个child(区分row or cloumn等是children),而且会将child的widget居中显示
        child: new Column(
          // Column也是一个布局widget,他能够有多个子widget
          // Column 有不少的属性去控制他的大小以及子widget的位置,这里咱们使用mainAxisAlignment来让children在垂直线上居中,
          // 这里的主轴就是垂直的,由于Column就是垂直方向的,这里能够大概想象为display:flex,flex-directions:column,align-item,justifyContent。。。
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new Text(
              'Hello World!',
              style:TextStyle(
                fontSize:24.0,
                color: Colors.redAccent,
                decorationStyle:TextDecorationStyle.dotted,
                fontWeight: FontWeight.bold,
                fontStyle: FontStyle.italic,
                decoration: TextDecoration.underline
              )
            ),
            new Text(
              'You have pushed the button this many times:',
            ),
            new Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: new Icon(Icons.add),
      ),//最后这个逗号有利于格式化代码
    );
  }
}

img
注释上基本已经加了,这里重点说下,StatefulWidget和StatelessWidget.

  • Stateless widgets 是不可变的,这意味着它们的属性不能改变——全部的值都是 final
  • Stateful widgets 持有的状态可能在 widget 生命周期中发生变化,实现一个 stateful widget 至少须要两个类:1)一个 StatefulWidget 类;2)一个 State 类,StatefulWidget 类自己是不变的,可是 State 类在 widget 生命周期中始终存在
  • 若是须要变化须要从新建立。StatefulWidget能够保存本身的状态。那问题是既然widget都是immutable的,怎么保存状态?其实Flutter是经过引入了State来保存状态。当State的状态改变时,能从新构建本节点以及孩子的Widget树来进行UI变化。注意:若是须要主动改变State的状态,须要经过setState()方法进行触发,单纯改变数据是不会引起UI改变的。

还有关于key的部分这里就不作介绍了,其实就相似与react中key的概念,便于diff,提升效率的。
具体能够查看 Key

到这里,咱们看到了Flutter的一些基本用法,Widget的套用、样式的编写、事件的注册,若是再学习下一些路由、请求、缓存是否是就能够本身开发APP了呢img

OK,强化下编写界面,咱再来些demo吧~

布局Widget

img

本身写的后,发现跟官网实现方式不一样,代码地址

具体实现能够参照官网教程

这里再也不赘述,下面咱们说下对于布局的理解和感觉以及经常使用布局widget。

从一个前端的角度来讲,说到画界面,可能仍是对布局这块比较敏感

img

img)

固然,这里咱们仍是说下目前经常使用的flex布局,基本拿到页面从大到小拆分后就是如上图。

因此Widget布局其实也就是Row和Column用的最多,而后因为Flutter一切皆为组件的理念,可能会须要用到别的类css布局的Widget,譬如:Container。其实咱就理解为块元素吧!

下面简单演示下一些经常使用的Widget,这里就不在赘述Row和Column了。传送门:布局Widget

Container

能够添加padding、margin、border、background color、一般用于装饰其余Widget

img

代码连接 Nealyang/flutter

class MyHomePage extends StatelessWidget{
  @override
  Widget build(BuildContext context){
    Container cell (String imgSrc){
      return new Container(
        decoration: new BoxDecoration(
          border:Border.all(width:6.0,color:Colors.black38),
          borderRadius: BorderRadius.all(const Radius.circular(8.0))
        ),
        child: Image.asset(
          'images/$imgSrc',
          width: 180.0,
          height: 180.0,
          fit: BoxFit.cover,
        ),
      );
    }

    return Container(
      padding: const EdgeInsets.all(10.0),
      color: Colors.grey,
      child: new Column(
        mainAxisSize: MainAxisSize.min,
        children:<Widget>[
          new Container(
            margin: const EdgeInsets.only(bottom:10.0),
            child: new Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children:<Widget>[
                cell('1.jpg'),
                cell('2.jpg')
              ]
            ),
          ),
          new Container(
            margin: const EdgeInsets.only(bottom:10.0),
            child: new Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children:<Widget>[
                cell('3.jpg'),
                cell('4.jpg')
              ]
            ),
          ),
        ]
      ),
    );
  }
}

该布局中每一个图像使用一个Container来添加一个圆形的灰色边框和边距。而后使用容器将列背景颜色更改成浅灰色。

GridView

可滚动的网格布局,理解为display:grid

GridView提供两个预制list,当GridView检测到内容太长时,会自动滚动。若是须要构建自定义grid,但是使用GridView.countGridView.extent来指定容许设置的列数以及指定项最大像素宽度。

img

代码连接 Nealyang/flutter

List<Container> _buildGridTileList(int count) {

  return new List<Container>.generate(
      count,
      (int index) =>
          new Container(child: new Image.asset('images/${index+1}.jpg')));
}

Widget buildGrid() {
  return new GridView.extent(
      maxCrossAxisExtent: 150.0,
      padding: const EdgeInsets.all(4.0),
      mainAxisSpacing: 4.0,
      crossAxisSpacing: 4.0,
      children: _buildGridTileList(10));
}

class MyHomePage extends StatelessWidget{
  @override
  Widget build(BuildContext context){
    return  new Center(
        child: buildGrid(),
      );
  }
}

如上是指定maxCrossAxisExtent,咱们能够直接去指定列数,例如官网的代码实例:

new GridView.count(
  primary: false,
  padding: const EdgeInsets.all(20.0),
  crossAxisSpacing: 10.0,
  crossAxisCount: 3,
  children: <Widget>[
    const Text('He\'d have you all unravel at the'),
    const Text('Heed not the rabble'),
    const Text('Sound of screams but the'),
    const Text('Who scream'),
    const Text('Revolution is coming...'),
    const Text('Revolution, they...'),
  ],
)

经过crossAxisCount直接指定列数。

Stack

层叠布局,position为absolute的感jio~

使用Stack来组织须要重叠的widget。widget能够彻底或部分重叠底部widget。子列表中的第一个widget是base widget; 随后的子widget被覆盖在基础widget的顶部。Stack的内容不能滚动。有点相似于weex中的设置了absolute的感受。底部组件永远在上面组件的上面。

ListView

可滚动的长列表,能够水平或者垂直。

Card

Material风格组件,卡片,AntD啥的组件库常常会出现的那种组件。

在flutter中,Card具备圆角和阴影,更改Card的elevation属性能够控制阴影效果。

ListTile

Material风格组件,我理解为经常使用的列表Item的样式,最多三行文字,可选的行前、行尾的图标

img

代码连接 Nealyang/flutter

总结

从目前我我的浅薄的Flutter技能来讲,最大的困难多是找不到合适的Widget去实现想要的布局或者效果,甚至包括css样式做用于那个Widget,譬如Opacity是一个widget而不是一个css样式~

因此对于Flutter,咱们仍是要多折腾,多些demo,相似网上不少仿xxxApp等~

对于画界面,更多的还能够参看下官网教程:Flutter for Web开发者

一切才刚刚开始

Flutter一切基于Widget,搞定widget就比如,搞定英语单词同样,单词、词组都贼6了还怕英语?

别急别急,借用张晟哥的图来给你们消消火气~

widgets

因此说,Flutter有一个庞大的组件体系,须要花费很是多的时间去梳理。

!更重要的是:多实践

原本最后一章是本身写的一个demo的讲解~

惋惜时间评估不许确,漏评估了假期惰性。。。考虑篇幅,后面补上仿XXX的Demo吧~~

img

参考连接 && 好文推荐

Demo 推荐

相关文章
相关标签/搜索