前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >简单了解Flutter

简单了解Flutter

作者头像
写代码的阿宗
发布2020-08-24 11:00:42
8250
发布2020-08-24 11:00:42
举报

距离Flutter正式版出来已经有很长的时间了,目前大家对于Flutter的呼声也是很高,就算是平时不了解移动开发的朋友们也开始好奇Flutter究竟是个什么东西。就连我朋友的老板都开始问,公司产品能不能换成Flutter来开发?

那么Flutter究竟是什么呢?它是一个声明式的移动UI框架,附带了自己的渲染引擎,类似于React框架自带了浏览器渲染引擎的感觉。它可以使app的界面编写更加简单直接,且不必在UI设计上做妥协。自带渲染引擎听起来侵入性比较强,没有使用对应平台的渲染机制,不过从RN的现状看来,通过bridge的形式依附于对应平台的渲染机制在性能上体验不佳,而Flutter会直接编译成native code,从理论上来说,跟我们用Java开发app相比不会有性能上的损失,写界面也变得更简单了。

Flutter使用Dart这门语言进行开发,Flutter本质上也就是个Dart类库。所有的控件,所有的代码都是用Dart编写的。一开始我很拒绝Dart这门语言,一个默默无闻好多年的语言,跟着Flutter才为人所知晓,谷歌推了这么久的kotlin,用kotlin来开发多好啊,我们学习迁移的成本也能大大降低。在这一年多的Flutter学习过程中,我发现谷歌这么做也有自己的考虑。首先Dart是谷歌自己的语言,想想它跟Oracle的官司打了多少年。其次Dart同时支持AOTJIT编译,JIT使得我们可以快速修改原型,我们做的修改一秒不到就可以更新到我们的设备上,而AOT保证我们发布的时候app不会有不必要的性能损失。那为什么不用go呢?算了吧,go只适合系统编程?。

Flutter宣称Everything is Widget,这句话对,也不全对,在Flutter里面,主题,边距,图片,动画等等都是通过widget来实现的,任何UI效果都可以抽象成Widget,其实在Flutter里面是有其它对象的,比如Element,比如RenderBox,(这些东西是什么我们下回再说)只不过对于我们开发者而言,我们日常开发中需要关心的就只是Widget了。Flutter中的Widget基本上可以分为两大类:StatefulWidgetStatelessWidget。这俩的区别可以直接从它们的名字上看出来,一个有状态,一个无状态。有状态的Widget会自己维护一些逻辑数据,而无状态的widget的内容由它的parent传递过来。简单来说,一个Widget如果包含了业务逻辑数据,这些数据在它销毁重建的时候需要做一些处理,那它就是有状态的。比如我们的购物车,它需要记录里面的商品的数量,它就是有状态的,而一个提交订单按钮它就是无状态的,它只关心在被点击的时候执行一个回调。两者在生命周期回调上也有所不同。有的同学第一次接触Flutter的可能觉得很绕,没关系,等会儿我们来简单上手一个小例子感受一下就懂了。

StatelessWidget会调用它的build方法来描述它的view,而StatefulWidget有一个与之配套的State对象,它只会调用createState方法去创建一个State对象,在这个State对象里也有一个build方法来描述view。这些build方法都必须返回另一个Widget

当我们新建一个Flutter项目的时候,默认给我们生成了一个计数器的demo。我们接下来就通过把玩这个项目来感受下flutter的魅力。Flutter提倡组合优于继承,所有的复杂的widget都是通过组合细小的Widget而来,我们只需要在build方法里面组合不同的widget就能定义出自己想要的UI。而Flutter也给我们提供了丰富的控件,我们日常开发就是不断组合Widget来构建我们的app,就连我们的app本身也是一个巨大的widget,不会有什么特殊的类去组合Widget来构建我们的app。

创建完项目,目录下的文件似乎有很大的一坨:

counter_app
  |- android
  |- ios
  |- lib
    |- main.dart
  |- test
    |- widget_test.dart
  .gitignore
  pubspec.yaml
  pubspec.lock
  README.md

我们主要的代码目录在lib目录下,android跟ios目录只有在特定平台相关的代码时才会用到。初始项目代码很少但是注释很多,官方给我们详细注释了使用到的Widget的用途。

运行刚创建的项目:

初始界面

点击那个加号,屏幕中央的数字就会增加。

我们来看下我们的代码,在main.dart这个文件里,跟我们的Java的规则一样,程序的入口是一个main函数:

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

我们实际项目中main函数不会这么简单,我们会做一些全局的配置,不过现在我们暂且不管,现在这么多就足够啦,这个MyApp就是对应到我们安卓里的Application了,我们来分析一下。

虽然我们的app代码很少,可以学到的东西可不少:

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

我们可以看到,我们的app也是一个Widget,而且还是一个StatelessWidget,我们重写了build方法,返回了另一个Widget,它是一个MaterialApp,然后这个Widget还可以有自己的子Widget。通过这种方式我们的app其实就形成了一个Widget树,包含着其它包含子WidgetWidget。然后我们可以尝试修改它,比如把这个primarySwatch的颜色换掉:Colors.orange,然后只要我们按下Ctrl+S,修改分分钟在我们的设备上生效,主题颜色立马改变了,这就是Flutter宣传时吹爆的热加载的能力,惊艳吧?

修改主题颜色

再来看看这个MyHomePage,它是MaterialApp的home属性的值,MaterialApp是一个应用骨架,提供了很多MD风格的配置,这个配置会通过Widget树往下传给所有子Widget,它的home属性代表着我们的主界面,我们来看看:

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

  final String title;

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

代码量很少,它是一个StatefulWidget,只是重写了createState方法。在这个_MyHomePageState里,重写了build方法实际描述了它的view。StatefulWidget的生命周期比较有意思:

StatefulWidget 生命周期

创建后会立即调用createState方法,在这里会调用State的构造器,然后走initState方法,然后调用build方法去描述它的view。而且Flutter是一个响应式的框架,我们通过setState方法去更新一些状态,每当setState方法被调用的时候,状态会被标记为dirty,然后Flutter会重新绘制。也就是说,我们可以通过setState方法去通知界面更新。

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

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), 
  );
  }
}

我们可以看到,我们想要UI成什么样子我们就声明出来就好了,想要居中就声明,想要竖直排列就声明,很直观,对于UI的描述都在Statebuild方法里了,在这儿一切都是WidgetCenter是让子Widget居中的WidgetColunm是让其它Widget纵向排列的WidgetText是展示文字的Widget。为什么这么做呢,怎么不在Widget里面去实现build方法呢?因为Widget都是不可修改(immutable)的,StatelessWidget能够实现build方法是因为它所有的信息都来自于外界,它本身木有什么状态可修改,而StatefulWidget则需要维护自己的状态,状态更新的时候还需要重新绘制,所以State对象才存在并且还有一系列方法通知系统去重新绘制。

再来看看这个FAB,点击它会增加屏幕中间的数字。

floatingActionButton: FloatingActionButton(
  onPressed: _incrementCounter,
  tooltip: 'Increment',
  child: Icon(Icons.add),
)

使用起来也很简单,我们知道Flutter是个声明式的UI框架,我们只需要声明它的子Widget,在这儿就是个Icon,以及被点击的回调就好了。在这里我们声明了点击调用_incrementCounter这个方法,这个方法里会通过setState去更新状态并通知系统重绘,那么所有依赖_counter这个变量的view都会重绘。Flutter给我们内置了很多MD的图标,如果大家对MD的图标比较满意,那直接通过Icons这个类就可以获取,省的UI再切图了。

瞧瞧,简单这些点代码,我们就实现了一个美观的计数器app。但是感觉功能有点单调,可能我们还有点手痒痒想动手试试,来,我们来自己增加些功能,实现增加,减少,重置的功能。

首先我们在FAB上方添加两个按钮来实现数字的加减,我们知道我们的UI整体在一个叫Column的widget里面,我们的按钮横向排列,当然得放在一个Row里面啦。

Row(
  children: <Widget>[
    RaisedButton(
      child: Text('增加'),
      onPressed: _incrementCounter,
    ),
    RaisedButton(
      child: Text('减少'),
      onPressed: _decrementCounter,
    ),
  ],
)

这边的_decrementCounter_incrementCounter代码类似,不过是减少数值。

增加按钮

按钮是加上来了,但是颜色有点单调,而且太靠左了,居中显示更加好看一点。这是因为Row是一个flex Widget,默认在横向上占有它能占有的所有空间,占据整个屏幕的宽度无所谓,我们得让这俩按钮居中,这里我们就用到RowmainAxisAlignment属性了,用它来分配主轴上的空白空间。我们这里使用MainAxisAlignment.spaceAround,它会把空白空间分成两边小中间大的样子,大家可以自由的尝试一下这个枚举类的不同的值,感受他们的效果。

Row(
  mainAxisAlignment: MainAxisAlignment.spaceAround,
  children: <Widget>[
    RaisedButton(
      color: Colors.orange,
      child: Text('增加'),
      onPressed: _incrementCounter,
    ),
    RaisedButton(
      color: Colors.amber,
      child: Text('减少'),
      onPressed: _decrementCounter,
    ),
  ],
)

最后我们需要把FAB改成重置的效果,只需要写一个_resetCounter方法把_counter设为0就好啦。

void _resetCounter() {
  setState(() => _counter = 0);
}

图标也要替换掉,好在Flutter给我们内置了好多MD图标,Icons里面就有刷新图标:Icons.refresh。再次Ctrl+S:

最终效果

好了,我们的最终效果已经实现了,增加了加减,重置功能,顺便也体验了一把编写Flutter应用有多爽。Flutter代码的编写流程就是这么简单,就是组合!组合小的形成大的Widget,组合已有的形成之前没有的Widget。在初步了解了Flutter之后,有些同学可能好奇,Flutter不停地销毁Widget再重建,它是怎么做到快速绘制如原生般流畅的?放心,下次我们就来了解一下Flutter的渲染流程,了解它为什么不停地创建销毁Widget却仍然丝滑。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 写代码的阿宗 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档