1. 前言

Flutter是一个由谷歌开发的开源移动应用软件开发工具包,用于为Android和iOS开发应用,同时也将是Google Fuchsia下开发应用的主要工具。其官方编程语言为Dart。

同为跨端开发的react-native的语言是JavaScript,RN是通过原生之间桥接来实现,而flutter是通过dart虚拟机直接编译。

这篇就不和 React-Native 进行详细对比 ,而是从前端开发的角度来看flutter。

2. Dart简介

Dart 比 JAVA 简单,易于理解,比 JavaScript 更加规范,更加工程化,兼具静态和动态语言的一些特性。

  • 强类型,可以直接生命变量类型,也可以类型推断;支持可选类型,用户可以像JavaScript一样写弱类型的定义,也可以确定类型。你可以写出动态语言风格的代码,也可以写出类似于传统静态风格的代码。Dart的类型推导使用final,var,const,dynamic关键字;
  1. var修饰变量;
  2. final表示不可变的,修饰内置数据类型,值不可变;修饰对象表示引用不可变,使用到的频率很高;
  3. const是编译时常量,他表示始终不可变,无论修饰内置类型还是对象,或者是数据结构;
  4. dynamic是任意类型,有点像java里面的Object,Kotlin中的Any
  • 单线程异步事件模型, 独特的隔离区( Isolate ),可以实现多线程;Dart是基于单线程模型的语言。但是在开发当中我们经常会进行耗时操作比如网络请求,这种耗时操作会堵塞我们的代码,所以在Dart也有并发机制,名叫isolate。APP的启动入口main函数就是一个类似Android主线程的一个主isolate。和Java的Thread不同的是,Dart中的isolate无法共享内存,类似于Android中的多进程。
  • DartVM,具有极高的运行效率和优秀的代码运行优化;
  • 面向对象编程,一切数据类型均派生自 Object ;
  • 运算符重载,泛型支持;
  • 异步API: Future (async/await)和 Stream(异步事件流) 模型,可以简单实现高效的代码;
  • Minix 特性,可以更好的实现方法复用;
  • 在语法上,Dart 提供了很多便捷的操作,可以明显减少代码量。比如字符连接,可以直接 "my name is $name, age is $age",无需+号拼接,也无需做类型转换(相当于ES6的模板字符串)。

更多参见官方文档

3. 从前端的角度看Flutter 

对于前端来说,页面就分为 HTML+CSS+JS

页面结构和样式  Flutter for Web 

对于习惯了html+css结合chrome devtools 完成UI效果的前端开发来说,刚开始用这种方式极其不习惯。

本来简单的一个结构和一个样式,加个背景色,渐变这种,css轻松搞定,然而,用widget来就要嵌套好多层,只能说,习惯习惯就好了。。

3.1 先来认识一下Flutter

Hello word入门

lib/main.dart

import 'package:flutter/material.dart';

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

// StatelessWidget和StatefulWidget两种
// 两者的区别在于状态的改变,StatelessWidget面向那些始终不变的UI控件;而StatefulWidget则是面向可能会改变UI状态的控件。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter', // Title 是用来定义任务管理窗口界面所看到应用名字的
home: Scaffold(
appBar: AppBar( // 导航栏
title: Text('Welcome to Flutter'),
),
body: Center(
child: Text('Hello World'),
),
),
);
}
}

3.2 路由跳转传参

在前端传参很简单,是直接携带携带参数(params和query)跳转路由页面的

flutter里面的路由可以分成两种,一种是直接注册,不能传递参数。另一种要自己构造实例,可以传递参数。

分两种方法 push 和 pushNamed

3.2.1 静态路由的注册

return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter实例'),
routes: <String, WidgetBuilder> {
// 这里可以定义静态路由,不能传递参数
'/login': (BuildContext context) => new SecondPage(),
'/register': (BuildContext context) => new RouterHomePage(),
},
);

3.2.2 静态路由的使用

onPressed: () {
Navigator.of(context).pushNamed("/register"); // or
  Navigator.pushNamed(context, '/register')
},

3.2.3 动态路由的使用

Navigator.of(context).push(
MaterialPageRoute(builder: (_) {
return new SecondPage(title: '传参过去');
}));

3.2.4 关闭页面

Navigator.pop(context); // or
Navigator.of(context).pop();
Navigator.pop(context,"携带参数"); // 可携带参数

3.2.5 push , pushNamed 传参

Pass arguments to a named route

https://flutter.dev/docs/cookbook/navigation/navigate-with-arguments

用 ModalRoute.of()封装路由及传参方法

push方法封装,有一个缺点就是不能只在主页面定义导入跳转的页面,略有代码重复

用pushNamed封装参数传递方法,就比较方便,不用再每一个页面import将要跳转的页面

main.dart

import 'package:flutter/material.dart';
import 'package:app/pages/home/app.dart';
import 'package:app/pages/login/login.dart';
import 'package:app/pages/login/register.dart';
import 'package:app/pages/login/forget.dart';
import 'package:app/pages/home/search_recommend.dart';
import 'package:app/pages/home/search_result.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override
Widget build(BuildContext context) {
return MaterialApp(
title: 'myapp',
routes: {
"/register": (context) => RegisterPage(),
"/login": (context) => LoginPage(),
"/forget": (context) => ForgetPage(),
"/search": (context) => SearchRecommendPage(),
"/searchresult": (context) => SearchResultPage(),
},
theme: new ThemeData(primarySwatch: Colors.red),
home: AppPage(),
);
}
}

从login跳转到register的时候,携带参数,并在register页面显示相应参数

login.dart

import 'package:app/pages/login/register.dart'; // 用push方法的话就要用到这个

onPressed: () => {  // 点击按钮跳转事件
Navigator.push( // push方法--支持StatelessWidget和StatefulWidget
context,
MaterialPageRoute(
builder: (context) => RegisterPage(),
settings:RouteSettings(arguments: {'title': '手机号注册'}))) // or pushNamed 方法
// Navigator.pushNamed(context, '/register',
// arguments: {'title': '手机号注册'})
},

register.dart 接收参数

class _RegisterPageState extends State<RegisterPage> {
@override Widget build(context) {
// 参数的接收显示
final Map args = ModalRoute.of(context).settings.arguments; return Scaffold(
appBar: AppBar(
title: Text(args['title']),
),
body: Center(
child: Text('register'),
),
}
}

3.3 事件触发

3.3.1 触发按钮的点击事件 

https://book.flutterchina.club/chapter3/buttons.html

(1)RaisedButton 漂浮按钮 (2)FlatButton 扁平按钮 (3) OutlineButton  (4) IconButton

几种按钮,触发事件都是 onPressed 点击事件

3.3.2 手势识别GestureDetector

https://book.flutterchina.club/chapter8/gesture.html

常见事件:点击、双击、长按

3.3.3 toast , alert 等弹窗

toast可直接安装 fluttertoast 包进行使用  https://pub.dev/packages/fluttertoast

alert, confirm 弹窗使用

部分代码

import 'package:flutter/material.dart';
onTap: () => showDialog(
context: context,
builder: (_) => _generateAlertDialog("我是弹框的内容")),
) _generateAlertDialog(String contents) {
return AlertDialog(
title: Text('这是标题'),
content: Text(contents),
actions: <Widget>[
FlatButton(
child: Text('取消'),
onPressed: () {
Navigator.of(context).pop(); // 点击取消关掉弹窗
},
),
FlatButton(
child: Text('确认'),
onPressed: () {
Navigator.of(context).pop(); // 点击确认关掉弹窗再跳转到别的页面
        Navigator.of(context).pushNamed('/register');
},
),
],
);
}

4. 异步请求

flutter中的请求方式

  • Dart 原生的网络请求 HttpClient
  • 库 http
  • Flutter中文网发布的 dio

4.1 HttpClient

4.2 使用官方 http 库

https://flutter.dev/docs/cookbook/networking/fetch-data

4.3 使用 dio http请求框架 

https://book.flutterchina.club/chapter10/dio.html

dio比较流行的一个请求包,它支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传、文件下载等

先看简单的请求

import 'dart:async';
import 'package:dio/dio.dart'; var dio = new Dio(); void getHttp() async {
try {
Response response = await dio.get("http://www.google.com");
print(response);
} catch (e) {
print(e);
}
}

可以封装一个通用请求模板,利用拦截器一并处理通用模块-比如请求前加loading, 结束关闭loading, 统一网络请求错误处理等

utils文件夹下的 net.dart

import 'dart:async';
import 'package:dio/dio.dart'; Dio dio; class NetRequest {
static Future<Dio> instance() async{
if (dio == null) {
dio = new Dio();
}
//添加拦截器
dio.interceptors.add(
InterceptorsWrapper(
onRequest: (RequestOptions options) {
print("请求之前");
// Do something before request is sent
return options; //continue
},
onResponse: (Response response) {
print("响应之前");
// Do something with response data
return response; // continue
},
onError: (DioError e) {
print("错误之前");
// Do something with response error
return e; //continue
},
),
);
return dio;
} static Future get(String url, Map<String, dynamic> params) async {
var response = await (await instance()).get(url, queryParameters: params);
print(response.data.toString());
return response.data;
} static Future post(String url, Map<String, dynamic> params) async {
var response = await (await instance()).post(url, data: params);
print(response.data.toString());
return response.data;
}
}

发起请求

import 'package:flutter/material.dart';
import 'package:app/utils/net.dart'; class MyFriends extends StatefulWidget {
MyFriends({Key key}) : super(key: key); @override
_MyFriendsState createState() => _MyFriendsState();
} class _MyFriendsState extends State<MyFriends> {
void _getInfo() async {
Map list = await NetRequest.get(url,{});
print(list); // 发起get请求之后获得的数据
} @override
void initState() {
super.initState();
_getInfo();
} Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Text('friends'),
),
);
}
}

post 请求同理

4. 开发者工具

https://flutter.github.io/devtools

Dart DevTools还是预览版,目前功能也还比较简单

VsCode   Dart: Open DevTools命令打开

5. 后记

刚开始写flutter各种不习惯,和js的模式不一样,更偏向于后端语言,作为跨端语言的新秀,又有Google这颗大树,设计之初就是作为跨多终端来实现的,这两年的发展也很快,国内很多团队有些已经在使用了。

官方发布版本也很勤,随着生态的逐渐完善,还是有很大的发展空间的,值得期待。

参考:

Flutter 官网

科普Dart语言

Flutter 中文网

最新文章

  1. kettle定时任务_第三方合作方有订单自动发送邮件通知_20161214
  2. redhat6.2下的ssh密钥免密码登录(原创)
  3. Uva 116,单向TSP
  4. ios开发之触碰动画效果
  5. nginx模块开发(18)—日志分析
  6. overflow-x: scroll;横向滑动详细讲解
  7. sql万能密码
  8. Head First Python-python面向对象
  9. PS把图片P到老树干上,变成老树成精!
  10. Tribonacci UVA - 12470 (简单的斐波拉契数列)(矩阵快速幂)
  11. Error, some other host already uses address 192.168.0.202错误解决方法
  12. MySQL之多表查询练习 与基本查询基础
  13. Hiberante持久化对象的3种状态
  14. CF1142E/1143B Lynyrd Skynyrd
  15. databinding在android studio2.3版本后不再默认支持使用
  16. http超文本传输协议,get与post区别
  17. Java的日志模块
  18. linux 复制文件夹内所有文件到另一个文件夹
  19. Java图片上查找图片算法
  20. Spring Security 介绍与Demo

热门文章

  1. 关于elasticsearch使用G1垃圾回收替换CMS
  2. Oracle数据库 常用的触发器命令
  3. NMS的实现代码详解
  4. 79.mobile/js---手机端分享调用功能的实现(只能在真机上测试有效)
  5. Java 之 Session 包含验证码登录案例
  6. mysql高级用法(1)- mariadb的主从搭建
  7. C# 认识 接口
  8. Pandas 数据筛选,去重结合group by
  9. springboot+vue2.x 解决session跨域失效问题
  10. Linux 服务控制与运行级别