目录
前言
接上一篇博客,入门Flutter(一)中的内容相对来说更加全局一些,并没有涉及到具体控件的使用,本篇入门会更加深入一些,不过还是停留在总览的地步,不会对每一个控件进行深入探究。
正文
如何监听Activity中的生命周期事件
在Android中我们通过Activity自带的方法或者在Application中注册ActivityLifecycleCallbacks接口进行生命周期监听。在Flutter中当然没有相关的概念(因为连Activity的概念也没有)
不过Flutter还是有方法去监听app状态的,否则就没办法响应切换到后台这种情况了。
先来说说监听方法,我们可以通过继承WidgetsBindingObserver,该类中存在一个回调方法didChangeAppLifecycleState,用于表示当前应用状态改变。(当然WidgetsBindingObserver 中还有其他很多状态回调,比如一个route 被push 或者pop,比如横竖屏变化,比如用户locales切换,是否存在内存压力,辅助功能切换等。)
具体代码如下
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
class LifecycleWatcher extends StatefulWidget {
@override
_LifecycleWatcherState createState() => _LifecycleWatcherState();
}
//多重继承了 WidgetsBindingObserver
class _LifecycleWatcherState extends State<LifecycleWatcher>
with WidgetsBindingObserver {
AppLifecycleState _lastLifecycleState;
//相当于初始化
@override
void initState() {
super.initState();
//注册应用声明周期监听
WidgetsBinding.instance.addObserver(this);
}
//相当于销毁
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
//监听应用声明周期方法
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
print("current state is $state");
setState(() {
_lastLifecycleState = state;
});
}
@override
Widget build(BuildContext context) {
if (_lastLifecycleState == null)
return Text('This widget has not observed any lifecycle changes.',
textDirection: TextDirection.ltr);
return Text(
'The most recent lifecycle state this widget observed was: $_lastLifecycleState.',
textDirection: TextDirection.ltr);
}
}
class MyApp extends StatelessWidget {
static const platform = const MethodChannel('app.channel.shared.data');
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: Center(child: LifecycleWatcher()),
//添加一个悬浮按钮,该按钮点击后触发_updateText方法
floatingActionButton: FloatingActionButton(
onPressed: _updateText,
tooltip: 'Goto Next',
child: Icon(Icons.update),
),
),
);
}
/**
* 这里定义一个native方法,用于打开一个新的activity,这个activity是一个dialog模式的,意味着会调用当前
* activity的 onPause,但是不会调用onStop
*/
void _updateText() async{
await platform.invokeMethod("gotoNext");
}
}
void main() {
runApp(MyApp());
}
native生命周期 | flutter生命周期 | |
打开app | onCreate->onStart->onResume | 无 |
打开dialog actiivty | onPause | AppLifecycleState.inactive |
关闭 dialog actiivty | onResume | AppLifecycleState.resumed |
home | onPause->onStop | AppLifecycleState.inactive -> AppLifecycleState.paused |
返回app | onStart->onResume | AppLifecycleState.inactive -> AppLifecycleState.resumed |
返回键退出 | onPause->onStop->onDestory | AppLifecycleState.inactive -> AppLifecycleState.paused |
先看看Flutter有哪几种状态 (这个地方就非常困惑了,因为官方文档上写的和我实际测试的存在区别,基于不迷信书本的理念,我实事求是贴出观测情况,有错误还请大家指正下)
AppLifecycleState.inactive 应用处于非活动状态,并且没有接收到用户输入。在Android中,这对应应用在前台非活动状态,比如分屏应用,电话,画中画应用,系统对话框或者其他窗口。类似于onPause,但是又不完全是,因为在返回app的时候也会先进入inactive状态,然后再resumed。
AppLifecycleState.paused 当应用对于用户不可见,不响应输入,并且在后台运行。相当于android中的onStop 。处于这个状态的app随时都会进入suspending挂起状态。
AppLifecycleState.resumed 相当于android中的onPostResume(onResume之后执行),表示应用可见并且响应输入.
AppLifecycleState.suspending 应用处于挂起状态。暂时还没有见到这种状态。
如何在一个widget上添加onClick事件
在Flutter中有两个方式来添加点击事件:
1.如果widget本身支持事件分发,比如RaisedButton,它有一个onPressed参数,这种情况我们可以直接使用
@override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: () {
print("click");
},
child: Text("Button"));
}
2.如果widget本身不支持事件分发,那么就需要把这个控件包裹在一个GestureDetector控件中,并且响应onTab参数。
class SampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GestureDetector(
child: FlutterLogo(
size: 200.0,
),
onTap: () {
print("tap");
},
),
));
}
}
响应其他手势事件
实际上我们已经在前面签名版的时候监听过一些手势事件了,这里列举一下GestureDetector能够响应的事件
点击:
onTabDown -> 类似于ACTION_DOWN
onTabUp -> ACTION_UP
onTab -> 点击事件
onTabCancel ->点击被取消,触发了onTabDown,但是不会触发onTabUp 和 onTab
双击:
onDoubleTab
长按
onLongPress
垂直拖动
onVerticalDragStart 和屏幕接触,并且可能会开始垂直的移动
onVerticalDragUpdate 和屏幕接触,在垂直方向上进一步移动
onVerticalDragEnd 之前与屏幕接触的指针不再于屏幕接触,并且在停止接触时有一个移动速度
水平拖动
onHorizontalDragStart
onHorizontalDragUpdate
onHorizontalDragEnd
导入三方字体
我们可以到Google Fonts下载各种字体
首先我们需要先准备好我们的字体文件ttf,并且将其放入Flutter app下面,比如放在fonts文件夹中
awesome_app/
fonts/
Raleway-Regular.ttf
Raleway-Italic.ttf
RobotoMono-Regular.ttf
RobotoMono-Bold.ttf
和android中直接加载字体到textview中不同,我们需要在pubspec.yaml文件中注册我们的字体.
fonts:
- family: Raleway
fonts:
- asset: fonts/Raleway-Regular.ttf
- asset: fonts/Raleway-Italic.ttf
style: italic
- family: RobotoMono
fonts:
- asset: fonts/RobotoMono-Regular.ttf
- asset: fonts/RobotoMono-Bold.ttf
weight: 700
family表示字体的名字,我们在TextStyle对象(用于确定字体样式的对象)的fontFamily中填入family值来使用这个字体。
asset指向字体文件的地址。在打包时这些字体文件会被打包到应用程序中。
同一种字体可以有不同的轮廓。
weight 用来用来表示字体的粗细,值在100到900之间,是100的整数倍。我们可以在TextStyle的fontWeight中设置这个值。
style 表示字体是斜体还是正常,可以在TextStyle的fontStyle中指定。
我们刻通过在主题中设置fontFamily来改变默认字体,当然也可以在单独的控件中使用
MaterialApp(
title: 'Custom Fonts',
// Set Raleway as the default app font
theme: ThemeData(fontFamily: 'Raleway'),
home: MyHomePage(),
);
或者
Text(
'Roboto Mono sample',
style: TextStyle(fontFamily: 'RobotoMono'),
);
如果我们给TextStyle设置的weight或者style没有对应的asset,那么系统就会自动选择这种字体里的一种风格。
如何在Flutter中使用NDK
很遗憾,当前在Flutter中没有办法直接调用c++的代码,所有c++代码都需要经过native层的中转,创建custom plugin或者直接MethodChannel。
如何使用Shared Preferences
这是一个Flutter插件,能够在NSUserDefaults(ios中的数据存储)和Shared Preferences的功能.
如何使用SQLite
如何使用消息推送
这个目前Flutter对于Firebase还是有插件的,但是其他的三方推送就没有插件了,只能自己开发或者建立Channel
结语
至此,我们已经入门了Flutter,随着学习的深入,发现Flutter并不是一个简单的UI替代方案,它已经是一整套完整的开发流程。另外最近Flutter的火热程度感觉急速上升,或许真能在移动开发界掀起颠覆也说不定。另外,据说Flutter也在向其他平台进军,它的目标是整合所有前端开发(PC应用,网页前端等等)。并且在2019年的I/O大会上还会有更多的分享。