Flutter快速入门
前言
Flutter安装及Hello World请看官方文档,本教程不做介绍
Flutter与其它移动开发框架
原生开发框架工作原理图
基于Webview框架的工作原理图
RN工作原理图(严重依赖OEM组件)
Flutter工作原理图
Flutter与其它方案对比
- Flutter与JS解决方案一样,使用AOT(提前编译),而不是JIT(实时编译)
- 与其它方案相比,Flutter性能要好得多
- Flutter消除了Bridge,且不依赖于OEM平台
- Flutter允许自定义组件使用屏幕上的所有像素,即使用Flutter开发的应用程序在安卓和iOS的每个版本上都显示相同的内容
- Flutter使用Widgets Rendering来处理画布(canvas)和事件(events),使用Platform Channels来使用服务(位置、蓝牙等)
- Flutter Hot Reload使用了Dart的JIT编译特性
Flutter的四种调试方式
Dart analyzer
可以创建analysis_options.yaml
文件,指定配置,有助于编写更好的Flutter代码
Dart observatory
可以通过添加断点或者debugger()
语句,中断应用执行进行调试,更多说明
Visual debugging
- 启用
debugPaintSize
选项,在入口文件处,指明debugPaintSizeEnabled
为true
,代码及效果如下:
void main() {
debugPaintSizeEnabled=true;
runApp(MyApp());
}
- 类似的还有启用
debugShowMaterialGrid
,进行MaterialApp的网格调试 - 启用
showPerformanceOverlay
,将展示性能图,性能图分为上下两部分。上部为GPU线程花费的时间,下部为CPU线程花费的时间
Flutter widget inspector
类似于Web调试的浏览器选择工具,选择后效果如下
Flutter的Widgets
说明
Flutter UI里一切皆Widgets,写过React及Styled-Components应该很容易理解,不过Flutter的部分样式也是由Widget组成,例如:Padding、Center等
常用Widgets列表
使用示例
Center(
child: Container(
height: 200.0,
width: 200.0,
child: Image.network("https://flutter.io/images/flutter-mark-square-100.png"),
),
),
Container可以用于条件表达式展示一个空组件,如
function getIcon(bool condition) {
if (condition == true) return Icon(Icons.edit);
else return Container();
}
Scaffold:Scaffold用于展示drawers, snackbars, bottomsheets, floating-action buttons等,使用示例
RaisedButton(
onPressed: () {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text("HELLO!"),
));
},
child: Text("BUTTON"),
color: Colors.blue,
),
GestureDetector:为非按钮组件添加交互性
Stateless widget & Stateful widget
Stateless widget:无状态的小组件,示例
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
Stateful widget:有状态的小组件,示例(通过setState更改state)
class _MyHomePageState extends State<MyHomePage> {
bool value = false;
@override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: value ? Colors.black : Colors.white,
appBar: new AppBar(
title: new Text(widget.title),
),
body: Center(
child: Switch(
value: value,
onChanged: (v) {
setState(() {
value = v;
});
}),
),
);
}
}
Flutter路由
通过Navigator.push()
打开新页面,通过Navigator.pop()
返回上一页,示例
onPressed() {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()), // SecondScreen为要跳转到的页面
);
}
注:要达到全屏模态框的效果可以将MaterialPageRoute
的fullscreenDialog
属性设置为true
Flutter中的约束
约束给出高度与宽度的最小值与最大值
RenderBoxes
有三种不同的渲染机制
- 试图以当期容器的最大宽高渲染,如:
ListView
、Center
等 - 试图以其子组件宽高作为自身宽高渲染
- 试图以特定宽度渲染,如:
Image
、Text
等
特殊的:当未向Container
构造函数传入宽高值时,它将倾向于最大渲染,但如果指明了宽高值,则使用特定大小。
特定的约束是无界(限)约束,这种情况下最大宽高将被设置为double.INFINITY
Flutter中的网络请求
示例
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<Post> fetchPost() async {
final response =
await http.get('http://localhost:3000/Movies/1');
if (response.statusCode == 200) {
return Post.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to load post');
}
}
class Post {
final int id;
final String movieName;
final int year;
final String category;
Post({this.id, this.movieName, this.year, this.category});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
id: json['id'],
movieName: json['movieName'],
year: json['year'],
category: json['category'],
);
}
}
void main() => runApp(MyApp(post: fetchPost()));
class MyApp extends StatelessWidget {
final Future<Post> post;
MyApp({Key key, this.post}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'JSON Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('JSON Fetch Data Example'),
),
body: Center(
child: FutureBuilder<Post>(
future: post,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.movieName);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return CircularProgressIndicator();
},
),
),
),
);
}
}
GET/POST的通用化原生封装
import 'dart:async';
import 'package:http/http.dart' as http;
import 'dart:convert';
class Api {
static bool useHttps = false;
static String prefix = "127.0.0.1:7001";
static void init({String prefix, bool useHttps}) {
Api.useHttps = useHttps;
Api.prefix = prefix;
}
static Future<dynamic> get({
String url,
Map<String, dynamic> param = null,
bool useDecode = true,
}) async {
url = (Api.useHttps ? 'https' : 'http') + '://' + prefix + url;
if (param != null) {
List<String> kv = [];
for (String key in param.keys) {
kv.add(key + '=' + param[key].toString());
}
url += '?' + kv.join('&');
}
print(url);
var client = new http.Client();
var resp = await client.get(url);
if (resp.statusCode != 200) {
return null;
}
if (useDecode) {
var json = new JsonDecoder();
return json.convert(resp.body);
}
return null;
}
static Future<dynamic> post({
String url,
Map<String, dynamic> param = null,
bool useDecode = true,
}) async {
url = (Api.useHttps ? 'https' : 'http') + '://' + prefix + url;
var client = new http.Client();
var resp = await client.post(url, body: param);
if (resp.statusCode != 200) {
return null;
}
if (useDecode) {
var json = new JsonDecoder();
return json.convert(resp.body);
}
return null;
}
}
其它
IDE技巧编写Flutter效率技巧
Android Studio
quickfix(快速修复)
: Alt + Enter
VS Code
quickfix(快速修复)
: Ctrl + .
Flutter包管理
Flutter打开webview
使用flutter官方的url_launcher包
Flutter 常用命令
$ flutter packages get # 在项目内部执行,安装pubspec.yaml指明的依赖
$ flutter channel # 查看渠道列表,及当前在用的渠道
$ flutter channel dev # 切换渠道为dev
$ flutter doctor # 执行flutter环境检查
$ flutter upgrade # 升级
$ flutter run -d [device ID] # 通过该设备启动应用