1、下载sdk
flutter官网:Install and run DevTools from Android Studio | Flutter
2、配置环境变量
as安装 dart和flutter插件,执行flutter doctor,到flutter module目录下(如:cd xxx_module),执行flutter pub get flutter clean
3、创建flutter module
flutter create -t module my_flutter
其中my_flutter为module的名字
flutter配置文件:pubspec.yaml
4、flutter hot restart
先到flutter module目录下,杀死app进程
然后执行flutter attach 命令,会出现如下
如果一直waiting,可以杀死app,重新运行一下代码
To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R".
最后,如果更改了某文件后,可以直接按键盘上的r键,进行热更新
5、flutter基础知识学习
首先引入了material库material是什么,是一个拥有基本样式的库。
runApp(runApp是什么)是程序的开始,这个函数需要一个Widget(Widget是什么),习惯叫他组件。
这里传入了一个嵌套的Widget,Center() 是一个居中的widget,Text() 是一个文本的widget,style顾名思义样式
import 'package:flutter/material.dart';void main() {
runApp(Center(
child: Text(
"Hello World",
style: TextStyle(fontSize: 36),
),
));
}
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("flutter"),
),
body: Center(
child: Text(
"Hello World",
style: TextStyle(fontSize: 36),
),
),
),
));
什么是runApp
Flutter应用程序是从调用这个函数开始的
什么是Widget
它就好像Vue中的组件,在Flutter中,万物皆Widget。
什么是Material
material是Google公司推行的一套设计风格,是一套规范。例如过场动画的样子已经定义了。
这里引用的material库,是已经实现了Material的Widget。
什么是Scaffold
翻译过来是脚手架的意思,他有appBar,body等属性,代表着导航栏跟页面内容
BuildContext:上下文
ListView.builder:列表。itemcount数量 itembuild是怎么展示
Expanded: 自适应宽高 扩展,如果要让里面的Column
占满外部Column
,可以使用Expanded
组件
onPressed :点击事件
6、flutter 调试
可视化:
官方文档:2.7 调试Flutter应用 | 《Flutter实战·第二版》
有时候我们想要像Android一样可以显示的看到自己布局的情况,那么很简单只需要设置---->系统---->开发者选项---->显示边界布局(选中)
就OK了,类似下图
debugPaintSizeEnabled = true; //显示边界布局然后自动import即可
修改完就是这样的
flutter 打断点
1、flutter attach,杀死app,重新进入
2、待黄色闪电标志亮起来后,去打断点即可
7、flutter 看层级Dev Tools
方案1:debug下面,点击蓝色图标
方案2:点击as右边栏的Flutter inspector
想看哪个层级,点击哪个即可
8、Flutter生命周期:面试官:知道 Flutter 生命周期?下周来入职! - 掘金
StatelessWidget和StatefulWidget
-
StatelessWidget: 没有状态改变的Widget,通常这种Widget仅仅是做一些展示工作而已;
-
StatefulWidget: 需要保存状态,并且可能出现状态改变的Widget;实现一个 StatefulWidget 至少需要两个类:
-
一个是 StatefulWidget 类。
-
另一个是 Sate 类。StatefulWidget 类本身是不可变的,但是 State 类在 Widget 生命周期中始终存在。StatefulWidget 将其可变的状态存储在由 createState 方法创建的 State 对象中,或者存储在该 State 订阅的对象中。
-
-
StatefulWidget 生命周期
-
createState:该函数为 StatefulWidget 中创建 State 的方法,当 StatefulWidget 被创建时会立即执行 createState。createState 函数执行完毕后表示当前组件已经在 Widget 树中,此时有一个非常重要的属性 mounted 被置为 true。
-
initState:该函数为 State 初始化调用,只会被调用一次,因此,通常会在该回调中做一些一次性的操作,如执行 State 各变量的初始赋值、订阅子树的事件通知、与服务端交互,获取服务端数据后调用 setState 来设置 State。
-
didChangeDependencies:该函数是在该组件依赖的 State 发生变化时会被调用。这里说的 State 为全局 State,例如系统语言 Locale 或者应用主题等,Flutter 框架会通知 widget 调用此回调。类似于前端 Redux 存储的 State。该方法调用后,组件的状态变为 dirty,立即调用 build 方法。
-
build:主要是返回需要渲染的 Widget,由于 build 会被调用多次,因此在该函数中只能做返回 Widget 相关逻辑,避免因为执行多次而导致状态异常。
-
reassemble:主要在开发阶段使用,在 debug 模式下,每次热重载都会调用该函数,因此在 debug 阶段可以在此期间增加一些 debug 代码,来检查代码问题。此回调在 release 模式下永远不会被调用。
-
didUpdateWidget:该函数主要是在组件重新构建,比如说热重载,父组件发生 build 的情况下,子组件该方法才会被调用,其次该方法调用之后一定会再调用本组件中的 build 方法。
-
deactivate:在组件被移除节点后会被调用,如果该组件被移除节点,然后未被插入到其他节点时,则会继续调用 dispose 永久移除。
-
dispose:永久移除组件,并释放组件资源。调用完 dispose 后,mounted 属性被设置为 false,也代表组件生命周期的结束。
-
flutter 生命周期ContainerLifeCycle:flutterboost监听生命周期
-
Init:
-
Appear:打开屏幕回到前台Foreground之后走
-
WillDisappear
-
Disappear:锁屏
-
Destroy
-
Background
-
Foreground 打开屏幕回到前台
-
Native->Flutter, ContainerLifeCycle.Init->ContainerLifeCycle.Appear Flutter->Flutter 旧的界面: ContainerLifeCycle.Appear(不会调用,只表示当前状态)->ContainerLifeCycle.Disappear-> 新的界面: ContainerLifeCycle.Init->ContainerLifeCycle.Appear Flutter->Native ContainerLifeCycle.Appear(不会调用,只表示当前状态)->ContainerLifeCycle.Disappear-> Flutter->Home->Flutter ContainerLifeCycle.Disappear->ContainerLifeCycle.Background(Home)->ContainerLifeCycle.Foreground->ContainerLifeCycle.Appear Flutter->back ContainerLifeCycle.Appear(不会调用,只表示当前状态)->ContainerLifeCycle.Disappear->ContainerLifeCycle.Destroy
9、Future:异步执行
Flutter中的异步其实就是用的Dart里面的Future,then函数,回调catchError这些东西 Future相关回调函数 future里面有几个函数: then:异步操作逻辑在这里写。 whenComplete:异步完成时的回调。 catchError:捕获异常或者异步出错时的回调。
如:
Future loadRequest(BuildContext context) async {
detailModel = await OrderDetailRequest.orderDetailInfoRequest(context,
params: widget.paramDict ?? {});
setState(() {});
return detailModel;
在我们平时开发中我们是这样用的,首先给我们的函数后面加上async关键字,表示异步操作,然后函数返回值写成Future, 逻辑前面加上一个await关键字,然后可以使用future.then等操作。
【结论】: 创建多个Future,执行顺序和和创建Future的先后顺序有关,如果只是单独的调用then,没有嵌套使用的话,和调用then的先后顺序无关。
【结论】: 执行顺序和和创建Future的先后顺序有关,如果有多个then嵌套执行,先执行外面的then,然后执行里面的then。
【结论】: 首先执行顺序和创建Future的先后顺序有关,如果遇到多个 then 嵌套,先执行外面的 then,然后再执行里面的then,如果then里面还有创建Future,要等到then执行完毕,之后执行Future。
10、flutter 加载图片
在工程根目录下创建一个类似images目录,并将图片 xxx.png 拷贝到该目录。
在pubspec.yaml中的flutter部分添加如下内容:
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
使用Material Design字体图标
Flutter默认包含了一套Material Design的字体图标,在pubspec.yaml文件中的配置如下
uses-material-design: true
# To add Flutter specific assets to your application, add an assets section,
# like this:
assets:
# assets-generator-begin
# assets/*
- assets/images/
- assets/images/home/
- assets/images/find_goods/
- assets/images/member/
- assets/images/order_detail/
- assets/images/shopcart/
- assets/images/common/
11、flutter typedef
翻译:
在 Dart 中,方法与 object,字符串,数字这几种类型一样都被称之为对象.一个 typedef 或者一个方法的 alias,为函数类型提供一个名称,在声明字段和返回类型时可以使用该名称。当函数类型分配给变量时,typedef 保留类型信息。 理解: 这句话的意思是说,如同 object,boolean,number 这几种被称为基本类型一样,typedef 的作用是可以自定义一种数据类型,并且这种数据类型属于 函数类型。同时 typedef 可以保留函数签名信息
使用方式:
typedef Fly = void Function(int value); // 定义一个类型(方法),该方法需要传入一个int参数
void main(){
Bird bird = Bird((int a){print(a);});//如果实参函数的类型不是该类型的话 编译不通过
bird.fly(3);
}
class Bird{
Fly fly;
Bird(this.fly);
}
12、flutter 输入框TextField
//输入框焦点
final FocusNode _focusNode = new FocusNode();
//编辑控制器
final TextEditingController _textEC = new TextEditingController();
//文本输入框获取焦点
void _textFieldGetFocus() {
FocusScope.of(context).requestFocus(this._focusNode);
}
//文本输入框失去焦点
void _textFieldLoseFocus() {
this._focusNode.unfocus();
}
13、flutter 关键字解析extends、with、implements
-
extends:继承Class
-
with:混入Class
-
implements:实现Class 这三种关系可以同时存在,但是有前后顺序: extends - with- implements extens在前,with在中间,implements最后
14、flutter mixin
mixin应该怎么理解呢,对Java系出身的我来说,这是一个新概念,各类资料的介绍也没找到一个清晰的定义。从个人理解来看,可以把它想象为Kotlin中的接口(和Java的区别是可以带非抽象的属性和方法),而多个mixin可以相互覆盖以实现组合,提供了非常大的灵活性,也可以达到类似多重继承的效果。
最简单的mixin
mixin TestMixin {
void test() {
print('test');
}
int testInt = 1;
void test2();
}
class Test with TestMixin {
@override
test2() {
print('test2');
}
}
void main() {
Test().test(); // test
print(Test().testInt); // 1
Test().test2(); // test2
}
15、flutter 在widget树中获取state对象
通过Context获取
// 查找父级最近的Scaffold对应的ScaffoldState对象
ScaffoldState _state = context.findAncestorStateOfType<ScaffoldState>()!;
通过GlobalKey获取
//定义一个globalKey, 由于GlobalKey要保持全局唯一性,我们使用静态变量存储
static GlobalKey<ScaffoldState> _globalKey= GlobalKey();
ScaffoldState _state = _globalKey.currentState;
16、flutter WidgetsBindingObserver
需要判断程序的前后台状态,这里主要使用WidgetsBindingObserver
进行判断
class _MyAppState extends State<MyApp> with WidgetsBindingObserver{
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this); //添加观察者
}
@override
void dispose() {
super.dispose();
print('YM--------dispose');
WidgetsBinding.instance.removeObserver(this); //添加观察者
}
17、flutter eventbus
//EventBusUtil.fire是发送eventbus,EventBusUtil.listen是接收eventbus
class EventBusUtil{
static EventBus _eventBus;
//获取单例
static EventBus getInstance(){
if(_eventBus == null){
_eventBus = new EventBus();
}
return _eventBus;
}
//返回事件的订阅者
static StreamSubscription<T> listen<T extends Event>(Function(T event) onData){
if(_eventBus==null){
_eventBus=EventBus();
}
//内部流属于广播模式,可以有多个订阅者
return _eventBus.on<T>().listen(onData);
}
//发送事件
static void fire<T extends Event>(T e) {
if(_eventBus == null){
_eventBus = EventBus();
}
_eventBus.fire(e);
}
}
abstract class Event {}
18、Flutter Callback
class CallMainPage extends StatefulWidget {
/// 传递的参数
final Map params;
CallMainPage({Key key, this.params}) : super(key: key);
@override
_CallMainPageState createState() => _CallMainPageState();
}
class _CallMainPageState extends State<CallMainPage> {
// 进来先走 initstate,再走 Widget build
// 再走 initstate里面的Future.delayed方法
@override
void initState() {
Future.delayed(Duration(milliseconds: 250), () {
this.getDefaultStoreInfo(null, () {
//callback处理
});
});
super.initState();
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return null;
}
void getDefaultStoreInfo(context, Function callBack) {
Map<String, dynamic> params = new Map<String, dynamic>();
if (callBack != null) {
callBack();
}
}
}
19、flutter List<Widget>
/// 数据主页面
Widget _buildDataWidget() {
return Container(
height: this.fixedHeight,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: _groupWidgetList(),
));
}
/// 主页面 header、body、bottom
List<Widget> _groupWidgetList() {
// header
Widget headerWidget = Container(
padding: EdgeInsets.only(
left: 12.0,
right: 12.0,
top: 15.0 + this.widget.barHeight,
bottom: 25),
child: buildCommonHeader(
20,
"我是header",
"更多",
() {
MMHomePageEvent().open(this.widget.realTimeGeneralModel?.scheme);
},
displayNameColor: Colors.white,
promoNameColor: ColorUtils.hexStringColor("#FFFFFF", alpha: 0.7),
onLeadingTap: () {
MMFHomePageSheet.showQuestionDetailSheet(context,
titleName: "我是数据",
realTimeGeneralList: this.realTimeGeneralList);
}));
// 内容部分
Widget bodyWidget = Expanded(
child: GridView.builder(
padding: EdgeInsets.only(top: 2),
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
mainAxisSpacing: 18,
crossAxisSpacing: 20,
childAspectRatio: childAspectRatio,
),
itemBuilder: (context, index) {
return _buildSaleItem(this.realTimeGeneralList[index]);
},
itemCount: this.realTimeGeneralList.length),
);
// 底部
Widget bottomWidget = InkWell(
child: Container(
padding: EdgeInsets.only(bottom: 16.0),
width: MediaQuery.of(context).size.width,
height: 25.0 + 10.0 + 16.0,
color: Colors.transparent,
child: Center(
child: Image.asset(R.asset_home_icon_arrow,
width: 25,
height: 10,
fit: BoxFit.cover,
alignment: Alignment.topCenter,
))),
onTap: _updadeIsExpand,
);
List<Widget> list = [];
list.add(headerWidget);
list.add(bodyWidget);
list.add(bottomWidget);
return list;
}