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关键字;
var
修饰变量;final
表示不可变的,修饰内置数据类型,值不可变;修饰对象表示引用不可变,使用到的频率很高;const
是编译时常量,他表示始终不可变,无论修饰内置类型还是对象,或者是数据结构;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()和onGenerateRoute()封装路由及传参方法
push方法封装,有一个缺点就是不能只在主页面定义导入跳转的页面,略有代码重复
用pushNamed封装参数传递方法,试了下只有无状态widget支持,有状态的貌似不支持
main.dart
import 'package:flutter/material.dart'; import 'package:app/pages/login/login.dart'; import 'package:app/pages/login/register.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { // 定义路由信息 final Map<String, Function> routes = { '/register': (context, {arguments}) => RegisterPage(arguments: arguments), // 可传参到register页面 '/login': (context) => LoginPage(), // 不设置传参login页面 }; @override Widget build(BuildContext context) { return MaterialApp( title: 'flutter', // 处理Named页面跳转 传递参数 onGenerateRoute: (RouteSettings settings) { // 统一处理 final String name = settings.name; final Function pageContentBuilder = this.routes[name]; if (settings.arguments != null) { // 如果参数不为空,则设置可传参 final Route route = MaterialPageRoute( builder: (context) => pageContentBuilder(context, arguments: settings.arguments)); return route; } else { // 不设置路由传参 final Route route = MaterialPageRoute( builder: (context) => pageContentBuilder(context)); return route; } }, theme: new ThemeData(primarySwatch: Colors.red), home: AppPage(), ); } }
从login跳转到register的时候,携带参数,并在register页面显示相应参数
login.dart
import 'package:app/pages/login/register.dart'; onPressed: () => { // 点击按钮跳转事件 Navigator.push( // push方法--支持StatelessWidget和StatefulWidget context, MaterialPageRoute( builder: (context) => RegisterPage(), settings:RouteSettings(arguments: {'title': '手机号注册'}))) // or pushNamed 方法 -- 支持StatelessWidget // Navigator.pushNamed(context, '/register', // arguments: {'title': '手机号注册'}) },
register.dart 接收参数–push方法的接收
class RegisterPage extends StatefulWidget { RegisterPage({ Key key, this.arguments, }) : super(key: key); final Map arguments; @override _RegisterPageState createState() => _RegisterPageState(); } 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'), ), } }
register pushNamed方法传递的接收
class ForgetPage extends StatelessWidget { ForgetPage({this.arguments}); final Map arguments; @override Widget build(context) { return Scaffold( appBar: AppBar( title: Text("参数:${arguments != null ? arguments['id'] : '0'}"), ), 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这颗大树,设计之初就是作为跨多终端来实现的,这两年的发展也很快,国内很多团队有些已经在使用了。
官方发布版本也很勤,随着生态的逐渐完善,还是有很大的发展空间的,值得期待。
参考: