18分钟轻松入门flutter,腾讯T3团队整理,带你摇身一变大前端

Flutter入门指南

(本文包含以下内容,阅读完需要约18分钟)

    1. Flutter是啥玩意儿?
    1. 移动端跨平台技术对比
      2.1 H5+原生APP
      2.2 RN & Weex
      2.3 Flutter
    1. Dart语言
    1. 环境配置
    1. Hello World
      5.1 创建项目
      5.2 项目结构
      5.3 启动模拟器
      5.4 启动项目APP
      5.5 简化版的Hello World
      5.6 给页面加上状态
      5.7 小结一下
    1. 路由
      6.1 单个页面的跳转
      6.2 更多页面跳转使用路由表
      6.3 路由传参
    1. widget
      7.1 Text
      7.2 Button
      7.3 Container
      7.4 Image
    1. 布局
      8.1 Row & Column & Center 行列轴布局
      8.2 Align 角定位布局
      8.3 Stack & Positioned 绝对定位
      8.4 Flex & Expanded 流式布局
    1. 动画
      9.1 简单动画:淡入淡出
      9.2 复杂一些的动画:放大缩小
    1. http请求
      10.1 HttpClient
      10.2 http
      10.3 Dio
    1. 吐吐槽
      11.1 墙
      11.2 组件过度设计
      11.3 嵌套太多不适应
      11.4 布局修改会导致嵌套关系修改
      11.5 Dart语言升级
      11.6 不能热更新
    1. 结语

1. Flutter是啥玩意儿?

Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。

  • 具有跨平台开发特性,支持IOS、Android、Web三端。
  • 热重载特性大大提高了开发效率
  • 自绘UI引擎和编译成原生代码的方式,使得系统的运行时的高性能成为了可能
  • 使用Dart语言,目前已经支持同时编译成Web端代码

到底值不值得跟进Flutter技术呢? 还是看下Flutter,Weex,ReactNative的搜索指数对比,大概就知道这个行业趋势了。

蓝色是Flutter,可以看出上升势头非常强劲。苦逼的前端就是这样,你不跟潮流,潮流就会把你抛弃。

2. 移动端跨平台技术对比

为啥会有Flutter这种东西? 他的原理是什么? 他是怎么做到高性能的? 要明白这些问题,我们不得不从几种移动端跨平台技术的对比讲起。

2.1 H5+原生APP

技术门槛最低,接入速度最快,热更新最方便的,自然就是H5方式。APP中提供一个Webview使用H5页面的Http直连。APP和H5可以相互独立开发,JS使用Bridge与原生进行数据通信,显示界面依赖Webview的浏览器渲染。
但是带来的问题也很明显,因为是需要远程直连,那么初次打开H5页面,会有瞬间的白屏,并且Webview本身会有至少几十M的内存消耗。

当然,作为前端开发人员,在H5方式可以使用SPA单页面、懒加载、离线H5等各种前端优化手段进行性能优化,以使得H5的表现更接近原生。但是首次的瞬间白屏和内存,Bridge的通信效率低下,始终是被技术框架给局限住了。

2.2 RN & Weex

由于H5的那些弊端,爱折腾的前端工程师,祭出了RN、Weex两个大杀器, 使用原生去解析RN、Weex的显示配置,显示层、逻辑层都直接与原生数据通信。
因为抛弃了浏览器,自然渲染性能、执行性能都提升了一大截。

但是,每次遇到显示的变更,JS都还会通过Bridge和原生转一道再做渲染的调整,所以Bridge就最后成为了性能的瓶颈。在实际项目中,特别是做一些大量复杂动画处理的时候,由于渲染部分需要频繁通信,性能问题变得尤为突出。

2.3 Flutter

不得不佩服Google开发人员的想象力,为了达到极致性能,Flutter更前进了一步,Flutter代码编译完成以后,直接就是原生代码,并且使用自绘UI引擎原生方式做渲染。
Flutter依赖一个Skia 2D图形化引擎。Skia也是Android平台和Chrome的底层渲染引擎,所以性能方面完全不用担心。因为使用Dart做AOT编译成原生,自然也比使用解释性的JS在V8引擎中执行性能更快,并且因为去掉Bridge,没有了繁琐的数据通信和交互,性能就更前进了一步。

3. Dart语言

学习Flutter,得先了解Dart。Dart语言曾经雄心勃勃的要替换Javascript,
但是发布的时机正好遇到JS的飞速发展,于是就逐渐沉寂,直到配合Flutter的发布,才又重新焕发了生机。

在最近2019年9月的一次Google开发者大会中,伴随着Flutter1.9的发布,目前的Dart也同时更新到了2.5版本,
提供了机器学习和对C跨平台调用的能力。总体来说,Dart语法,对于前端同学,上手还是很容易的,风格很像。

关于Dart语法,请移步传送门:Dart语法示例

4. 环境配置

无论学什么新语言,首先都是环境配置。由于Flutter出自Google,所以有一定门槛,如果在公司内安装,你还需要一个方便的代理切换工具,
开发环境推荐使用VSCode并安装Flutter和Dart插件。iOS开发者可借助AppUploader管理证书和配置文件,该工具支持自动化处理苹果开发者账号的证书创建、描述文件更新等繁琐流程,显著提升开发调试效率。
比如:Proxifier

安装教程,参照官网:Flutter安装指南

Flutter支持多种编辑器如:Android Studio , XCode。 但是既然作为支持跨双端的开发,个人还是推荐使用 VSCode

VSCode安装完成后,需要安装Flutter插件,和Dart插件. 在扩展窗口里,搜索Flutter,和Dart,点击“Install”即可,非常方便。

如果安装不上去,记得开启下代理。

5. Hello World

作为一个伟大的程序员,第一行代码总是从Hello World开始。

5.1 创建项目:

方法1:直接使用命令创建:

flutter create projectname

方法2:使用VSCode创建:

View -> Command Palette -> Flutter:New Project 即可

注意请先打开代理,否则你的创建进度,会一直被卡住。

5.2 项目结构

将项目先拖入VSCode,看下目录结构。自动创建完成的项目中,我们看到已经自带了Android,IOS相关的运行环境。

入口主文件是main.dart. 可以打开来先熟悉下,暂时不了解没关系,后面再讲。

还有一个重要的文件是pubspec.yaml ,是项目的配置文件,这个后续也会做修改。

5.3 启动模拟器

点击VSCode右下角的模拟器,启动模拟器。(VSCode会自动找到Android环境、IOS环境下的模拟器,以及真机环境)

5.4 启动项目APP

选中Main.dart, 点击Debug -> Start Debugging , 项目就会启动调试,并在模拟器里运行。

5.5 简化版的Hello World

讲道理,Flutter一上来就用StatefulWidget做一个自增的Demo,其实是对新手不太友好。
我还是喜欢循序渐进,先删掉那些复杂的自增逻辑,我们基于StatelessWidget 只做一个最简单的静态页面显示。(什么是StatefulWidget
和StatelessWidget?后面会说)

main.dart

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("我是Title"),
      ),
      body: Center(
        child: Text(
          'Hello World',
        ),
      ),
    );
  }
}

在上面的代码中,可以清楚看到,最简单的页面的层级关系:

MaterialApp -> MyHomePage -> Scaffold -> body -> Center -> Text

**Scaffold是啥?**他是Flutter的页面脚手架,你可以当HTML页面一样去理解,不同的是,他除了Body以外,还提供appBar顶部TitleBar、bottomNavigationBar底部导航栏等属性。

显示效果:

这是最简单的页面,没有交互,只有显示,但是实际业务场景中,是不太可能都是这种页面的,页面上的数据一般都是来自接口返回,然后再在页面上进行动态的渲染。
此时,就需要使用使用带状态的StatefulWidget了

5.6 给页面加上状态

给自己一个需求,按钮点击时,修改页面上显示的文字“Hello World” 变成“You Click Me”

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  
  MyHomePageState createState() => MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> {
  var msg = "Hello World"; //msg默认文字
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("我是Title"),
      ),
      body: Center(
        child: Column(
          children: [
            Text(msg), //根据变量值,显示文字
            FlatButton(
              color: Colors.blue,
              textColor: Colors.white,
              //点击按钮,修改msg的文字
              onPressed: () {
                setState(() {
                  this.msg = "You Click ME";
                });
              },
              child: Text(
                "Click ME",
                style: TextStyle(fontSize: 20.0),
              ),
            )
          ],
        ),
      ),
    );
  }
}

执行效果:

上面最关键的一段代码就是这个:

onPressed: () {
  setState(() {
    this.msg = "You Click ME";
  });
},

相信写过小程序的同学,对这个 setState 还是很眼熟的 _

5.7 小结一下

StatelessWidget:无状态变更,UI静态固化的Widget, 页面渲染性能更高。
StatefulWidget:因状态变更可以导致UI变更的的Widget,涉及到数据渲染场景,都使用StatefulWidget。

为啥要分两个? StatelessWidget拥有的功能,StatefulWidget都有了啊?

答案只有一个:性能、性能、性能

在StatefulWidget里,因为要维护状态,他的生命周期比StatelessWidget更复杂,每次执行setState,都会触发
window.scheduleFrame() 导致整个页面的widget被刷新,性能就会降低。

使用过小程序的同学在这点上应该有体会,在小程序的官方文档中,会强烈建议减少setData的使用频率,以避免性能的下降。
只不过flutter更是激进,推出了StatelessWidget,并直接在该Widget里砍掉了setState的使用。

页面结构关系如下:

6. 路由

实际的项目,是有多个不同的页面的,页面之间的跳转,就要用到路由了。 我们增加一个list页面,点击Home页的“Click
Me”按钮,跳转到列表页list。

6.1 单个页面的跳转

增加list.dart

import 'package:flutter/material.dart';

class ListPage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    //定义列表widget的list
    List<Widget> list = [];

    //Demo数据定义
    var data = [
      {"id": 1, "title": "测试数据AAA", "subtitle": "ASDFASDFASDF"},
      {"id": 2, "title": "测试数据bbb", "subtitle": "ASDFASDFASDF"},
      {"id": 3, "title": "测试数据ccc", "subtitle": "ASDFASDFASDF"},
      {"id": 4, "title": "测试数据eee", "subtitle": "ASDFASDFASDF"},
    ];

    //根据Demo数据,构造列表ListTile组件list
    for (var item in data) {
      print(item["title"]);

      list.add(ListTile(
          title: Text(item["title"], style: TextStyle(fontSize: 18.0)),
          subtitle: Text(item["subtitle"]),
          leading: Icon(Icons.fastfood, color: Colors.orange),
          trailing: Icon(Icons.keyboard_arrow_right)));
    }

    //返回整个页面
    return Scaffold(
      appBar: AppBar(
        title: Text("List Page"),
      ),
      body: Center(
        child: ListView(
          children: list,
        ),
      ),
    );
  }
}

在main.dart增加list页面的引入

import 'list.dart';

修改Home页的按钮事件,增加Navigator.push跳转

FlatButton(
  color: Colors.blue,
  textColor: Colors.white,
  onPressed: () {
    Navigator.push(context, MaterialPageRoute(builder: (context) {
      return ListPage();
    }));
  },
  child: Text("Click ME", style: TextStyle(fontSize: 20.0)),
)

核心方法就是:Navigator.push(context, MaterialPageRoute)

跳转示例:

6.2 更多页面跳转使用路由表

在MaterialApp中,有一个属性是routes,我们可以对路由进行命名,这样跳转的时候,只需要使用对应的路由名字即可,如:Navigator.pushNamed(context, RouterName)。点击两个不同的按钮,分别跳转到ListPage,和Page2去。

Main.dart修改一下如下

import 'package:flutter/material.dart';
import 'list.dart';
import 'page2.dart';

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

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      //路由表定义
      routes: {
        "ListPage": (context) => ListPage(),
        "Page2": (context) => Page2(),
      },
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  
  MyHomePageState createState() => MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("我是Title"),
      ),
      body: Center(
        child: Column(
          children: [
            RaisedButton(
              child: Text("Clikc to ListPage"),
              onPressed: () {
                //根据命名路由做跳转
                Navigator.pushNamed(context, "ListPage");
              },
            ),
            RaisedButton(
              child: Text("Click to Page2"),
              onPressed: () {
                //根据命名路由做跳转
                Navigator.pushNamed(context, "Page2");
              },
            )
          ]),
      ),
    );
  }
}

示例:

当我们有了路由以后,就可以开始在一个项目里用不同的页面,去学习不同的功能了。

6.3 路由传参

列表页跳转到详情页,需要路由传参,这个在flutter体系里,又是怎么做的呢?

首先,在main.dart里,增加详情页DedailPage的路由配置

//路由表定义
routes: {
  "ListPage": (context) => ListPage(),
  "Page2": (context) => Page2(),
  "DetailPage": (context) => DetailPage(), //增加详情页的路由配置
},

并修改ListPage里ListTile的点击事件,增加路由跳转传参,这里是将整个item数据对象传递

ListTile(
  title: Text(item["title"], style: TextStyle(fontSize: 18.0)),
  subtitle: Text(item["subtitle"]),
  leading: Icon(Icons.fastfood, color: Colors.orange),
  trailing: Icon(Icons.keyboard_arrow_right),
  onTap: () {
    //点击的时候,进行路由跳转传参
    Navigator.pushNamed(context, "DetailPage", arguments: item);
  },
)

详情页DetailPage里,获取传参并显示

import 'package:flutter/material.dart';

class DetailPage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    //获取路由传参
    final Map args = ModalRoute.of(context).settings.arguments;

    return Scaffold(
      appBar: AppBar(
        title: Text("Detail Page"),
      ),
      body: new Column(
        children: [
          Text("我是Detail页面"),
          Text("id: ${args['id']}"),
          Text("id: ${args['title']}"),
          Text("id: ${args['subtitle']}")
        ],
      ),
    );
  }
}

Demo效果:

7. widget

Flutter提供了很多默认的组件,而每个组件的都继承自widget。 在Flutter眼里:一切都是widget。 这句看起来是不是很熟悉?
还记得在webpack里,一切都是module吗? 类似的还有java的一切都是对象。貌似任何一个技术,最后都是用哲学作为指导思想。

widget,作为可视化的UI组件,包含了显示UI、功能交互两部分。大的widget,也可以由多个小的widget组合而成。

常用的widget组件:

7.1 Text

Demo:

Text(
  "Hello world",
  style: TextStyle(
    fontSize: 50,
    fontWeight: FontWeight.bold,
    color: Color(0xFF0000ff),
  ),
)

Text的样式,来自另一个widget:TextStyle。 而TextStyle里的color,又是另一个widget Color的实例。

如果用flutter的缩进的方法,看起来确实有点丑陋,习惯写CSS的前端同学,可以看看下面的风格:

Text("Hello world", style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold, color: Color(0xFF0000ff)))

写成一行,是不是就顺眼多了?这算前端恶习吗?_

7.2 Button

对于flutter来说,Button就提供了很多种,我们来看看他们的区别:

RaisedButton: 凸起的按钮
FlatButton:扁平化按钮


8.布局实战

8.3 Stack定位
Stack(
  children: [
    Positioned(
      left: 20,
      child: Icon(Icons.star)
    )
  ]
)

11.吐吐槽

  • 嵌套地狱:复杂UI可能导致10+层嵌套
  • 热更新缺失:线上问题修复依赖发版
  • 生态局限:部分原生功能仍需自行封装

12.结语

Flutter凭借优异的渲染性能和跨端能力正在成为主流选择。实际开发中可搭配AppUploader等工具链提升效率,其自动化打包和证书管理功能尤其适合需要频繁调试的iOS开发场景。虽然存在学习曲线,但长远来看仍是值得投入的技术方向。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值