此文章并不能构成体系,仅作为个人学习笔记使用
Dart 语言
一、特性
1,一切皆对象
Dart 是纯对象的编程语言,处理的所有值,包括基本类型函数都是堆绣
2,面向接口编程
Dart 核心思想是关注对象行为而非内部实现:
(1)没有final方法,允许重写除内置操作符以外所有方法
(2)语言是基于接口,而不是基于类。 所有类都隐含一个接口,能被其他类实现,处理数值、布尔、字符串
(3)Dart 把所有对象都进行了抽象封装,确保外部操作都通过存取方式改变对象状态
(4)构造函数允许对对象进行缓存,或从子类创建
3,类型可选
二、变量常量、类型、运算符
1,变量
var 定义,默认变量值为null
Dart 是强类型语言,和js这种弱类型语言明显区别:Dart 第一次将变量赋值为字符串,则后续不能将变量更改为其他类型【或用dynamic关键字修饰,可以改变类型】; js可以随便更改类型
2,常量
final / const 都可以定义常量,区别是 const 在编译时检查值,final 在运行时检查值
3,内置类型
(1)数值型:int / double 【没有float】
int 可以赋值给double,反过来则不行
(2)布尔型bool: true / false
(3)字符串
单引号、双引号、r 创建原始字符串、''' ''' 创建多行信息;
支持插值表达式 ${name}
(4)列表:集合和数组是同一个概念
//创建列表
var l1 = ['a','b','c']
var l2 = new List()
..add(1)
..add(2)
(5)键值对:Map,未明确指定类型时,默认Map<dynamic,dynamic>
(6)动态类型和Object
Dart 中所有对象的父类都是Object
支持 is !is as
4,运算符
(1)三目运算符 ?: ??
(2)取商运算符: ~/
(3)级联运算符: .. 链式调用
(4)自定义运算符
三、循环、遍历、异常、set&get
1, 循环
for while switch
3,异常
java 中 throw 的是一个异常对象 NullPointerException,Dart 中可以 throw 任何类型的异常
try{
}on String catch(e){// 预料到可能回抛出一个字符串类型异常
}catch(e){
}final{
}
4,set&get
如果属性是共有的,不用写set&get函数,可以直接使用;
如果属性是私有的['_'],需要手动实现set&get
四、函数、抽象、接口、继承、异步
1,函数
(1)main 顶层入口函数
(2)可选参数
void setUser( { String name,int age } ) //可不传,可传一,可传二
(3)可选位置参数
void setUser( { String name,int age, [String sex] } )
(4)默认参数
void setUser( { String name = 'admin' , @require int age} )
(5)函数可以作为参数传递
2,抽象 abstract
3,接口
Dart 中有接口,但没有接口声明,可以定义抽象类描述接口
4,继承
Flutter 只支持单继承,可用@override 重写,@super 调用超类
5,异步
Dart 是单线程的,执行耗时操作就会堵塞;因此使用Future 对象执行异步操作,搭配 async 和 await 关键字
(1)Future 是泛型对象,用来标志函数,返回值是 Future<T>
该函数将加入待完成的队列;当操作结束后,回返回值或错误
Future<String> getFileStr(File file){}
s_file.getFileStr(file)
..then((f){...})
..catchErroe((err){...})
..whenComplete() => {...}
(2)async await 减轻异步操作的工作量,减少嵌套地狱
tasks() async{
String task1Result = await task1('task1');
String task2Result = await task2(task1Result );
String task3Result = await task3(task2Result );
}
五、泛型、mixin
1,泛型
2,mixin
Dart 是单线程的,Flutter 引入 mixin 解决多继承的问题;用非继承的方式,来复用类中的方法
可以把自己的方法提供给其他类,而不用成为其父类
(1)当三者都有时,调用同一方法,运行顺序是: mixin extends implements
Flutter组件
一、基础组件
1,Text
2,Button
RaisedButton、FlatButton、IconButton、OutlineButton、FloatingActionButton
3,Icon
4,Image
//网络图片
Image(
image:NetworkImage('')
)
//本地图片
Image(
image:AssetImage('')
)
//图片文件
Image.file(
file('')
)
二、单一子元素组件
1,Container
(1)对齐方式: alignmen: Alignment.center/centerLeft/topCenter/topLeft...
(2)默认占满父组件,可以使用约束: constraints,控制Container组件大小,根据子组件内容适当调整大小
Container(
constraints:BoxConstraints(
maxHeight:200,
minHeight:100,
maxWidth:200,
maxWidth:100,
)
)
扩展到最大,则使用 constraints:BoxConstraints.extends
(3) padding margin
EdgeInsets.only({top bottom left right})
EdgeInsets.symmetric({ vertical:top/bottom, horizontal:left/right })
EdgeInsets.fromgLTRB() // 分别指定四个方向
EdgeInsets.All() //所有方向用同一个数值填充
(4)docoration: BoxDecoration
2,Padding
Flutter 淡化了Padding和Margin的区别
3,Align: 设置child 的对齐方式
(1)widthFactor 和 heightFactor : Align会随着这两个属性改变自己的尺寸
4,Center: 继承自Align
5,FittedBox: 类似于 ImageView
(1)fit: 缩放方式,类似于 scaleType
6,AspectRatio :根据设置,调整子元素child 的宽高比
AspectRatio 会首先在布局约束的范围内尽可能的扩展, aspectRatio 参考值
7,SingleChildScrollView :类似于 ScrollView
8,ConstrainedBox 约束性的组件,必须设置 constraints 属性
9,Baseline,基线组件,控制不相干的几个组件在同一个水平线上对齐
三、多子元素组件
1,Scaffold 脚手架,标准化布局容器
其结构集成了AppBar body bottomNavigationBar floatingActionButton drawer
2,AppBar 顶部导航栏
控制App路由,显示顶部标题栏

3,Row 和 Column
4,ListView
四种创建方式:
(1)直接使用
ListView(
itemExtent:30, // 子组件高度 【可以不设置,自适应;设置后效率更高】
children:<widget>[
Text('1'),
Text('2'),
Text('3'),
]
)
(2)ListView.builder
ListView.builder(
itemExtent:30,
itemCount:4,
itemBuilder:(context,position){
return Text('$position')
}
)
(3)ListView.separated 带分隔符
ListView.builder(
itemCount:4,
itemBuilder:(context,position){
return Text('$position')
},
separatorBuilder:(context,position){
return Container(
width:500,
height:20,
color:Colors.red
)
}
)
(4)ListView.custom 自定义功能
ListView.custom 可以通过 SliverChildListDelegate 来接收 IndexedWidgetBuilder ,并为 ListView 生成列表项,实现自定义功能
ListView.builder 和 ListView.separated 内部都是通过 ListView.custom 实现的
5,GridView
gridView 的创建,可使用 ListView 的创建方式
但其还有两个独特的方式
(1)设置每行固定展示两个item
GridView.count(
crossAxisCount:2,
mainAxisSpacing:10,
crossAxisSpacing:10,
children:<widget>[
Text('1'),
Text('2'),
Text('3'),
]
)
(2)设置每item最大像素宽度 maxCrossAxisExtent ,自动适配个数,能放下几个就放几个
GridView.extent(
maxCrossAxisExtent :130,
mainAxisSpacing:10,
crossAxisSpacing:10,
children:<widget>[
Text('1'),
Text('2'),
Text('3'),
]
)
6,CustomScrollView
可以包含不止一个滚动组件
7,Stack:绝对布局组件
8,IndexedStack: 通过 index 属性,直接切换子组件
类似于 PageView,没有动画过渡
9,Table
10,Flex: 弹性布局
flex 弹性系数,类似于 layout_weight
11,Wrap: 类似于Row,支持自动换行
12,Flow:承继Wrap 功能,主要用于自定义布局,或性能较高场景
四、其他组件
1,TextField 文本输入框
| 属性 | 说明 |
|---|---|
| controller | 输入框监听器,类似于 TextWatcher |
| decoration | 输入框装饰属性 |
| textAlign | 内容对齐方式 |
| textAlignVertical | 文本垂直对齐 |
| textDirection | 文字方向 |
| cursorColor cursorHeight cursorWidth | 光标 |
| showCursor | 是否显示光标 |
| obsureText | true 时为密码属性,展示 * |
| keyboardType | 限制输入的文本类型 |
| readOnly | 是否只读 |
| autofocus | 自动对焦 |
| maxLength | 最大文本长度 |
| maxLines minLines | 最大 最小行数 |
| onEditingComplete onSubminted | 编辑完成触发的回调 |
| onChanged | 文本改变时回调 |
2,TextFormField 文本输入框
TextField 本身不能设置默认值,也不支持表单数据的前置校验;TextFormField 针对TextField 又封装了一层
3,侧滑菜单 Drawer + ListTile
4,轮播 Swiper
状态管理
一、基础组件
Flutter 中一切皆组件Widget,组件分为 StatelessWidget 和 StatefulWidget 两大类。
从上到下构成 Widget树 , 每个Context 对应一个Widget,也就构成 Context树
1,StatelessWidget 无状态组件
特性:
(1)内部属性声明为final,无法更改,
(2)生命周期只有:初始化 -> build() 界面渲染
2,StatefulWidget 状态组件
特性:
(1)创建组件的时候,也会创建一个 state 对象,通过这个对象,和用户交互并刷新页面
(2)内部属性声明可以通过 setState() 方法更改,
(3)组件由两部分构成:主体部分、State部分
1)主体部分继承 StatefulWidget ; 但这里并没有 State 对象,因此主体部分的变量也是无法更改的
2)State 部分继承 State
(4)生命周期

二、State、Key
1,State 是对组件的行为和布局的描述
(1)生命周期
- initState: 用于初始化数据,和绑定controller; 此时未关联Context,无法访问Context
- didChangeDependencies : 在 state 对象依赖发生变化时调用;此时可以访问Context
- build :构建Widget树
- reassemble :debug 模式下,热重载时调用
- didUpdateWidget : 重新构建Widget 树时调用,一般用于检测新旧Widget属性,看看属性值是否改变
- deactivate :
- dispose : 永久移除时调用;在此处移除监听,释放资源
(2)生命周期场景
1)场景 1:打开页面:
- constructor
- createState
- initState
- didChangeDependencies
- build
2)场景 2:退出页面:
- deactivate
- dispose
3)场景 3:热重载:
- reassemble
- didUpdateWidget
- build
4)场景 4:横竖屏切换
- didUpdateWidget
- build
- didUpdateWidget
- build
2,Key
每个Widget 都有独有的key,在组件渲染时生成的。当 Widget 的属性发生变化时,Flutter 框架会根据Key来判断是否需要重新创建 Widget,还是复用已有的 Widget。
满足以下定义:
- 更新元素时,新提供的Widget 的key,必须与之前关联Widget的key相等
- 同一个父节点下,各个子节点的key,必须不同
- 要实现key的子类,必须继承 GlobalKey,或 LocalKey

(1)GlobalKey
GlobalKey是全局唯一的,它可以跨 Widget 树使用,用于获取子 Widget 的状态和方法。这在需要在不同 Widget 之间共享状态,或者在 Widget 树的不同层级访问某个 Widget 时非常有用。
(2)LocalKey
LocalKey用于在同一父 Widget 的子 Widget 之间进行唯一标识
1) ValueKey:通过一个值来标识 Widget
2) ObjectKey:基于一个对象来标识 Widget
3) UniqueKey 总是生成一个完全唯一的 Key,即使传入相同的值,每次调用都不同。它适合在动态添加或删除组件时,确保组件在重新排列时强制销毁和重建。
三、InheritedWidget
InheritedWidget 是很特殊的组件,支持跨级数据传递。支持在Widget 树中共享数据。
setState 方法用于通知 Flutter 更新 Widget 的状态,比如重建当前 Widget 及其子树,它是从上到下进行通知的,但作用域仅停留在当前的 StatefulWidget 范围内。
如果有两个同级的 StatefulWidget ,比如 WidgetA 和 WidgetB ,当 WidgetA 的数据发生改变,想要通知 WidgetB 对应改变。
InheritedWidget 控件的使用:左边栏和右边栏是两个同级的 Widget ,右边栏中点击修改名称,左边栏中显示对应的修改内容。
事件处理
一、原始指针
1,Pointer Event
原始指针需要通过命中测试,获取触摸的操作区域,找到对应的Widget
this.onPointerDown(PointerDownEvent event), //手指按下回调
this.onPointerMove(PointerMoveEvent event), //手指移动回调
this.onPointerUp(PointerUpEvent event),//手指抬起回调
this.onPointerCancel(PointerCancelEvent event),//触摸事件取消回调 -> 基本不会用到
2,PointerEvent
PointerDownEvent、PointerMoveEvent 、PointerUpEvent 、PointerCancelEvent 都是PointerEvent的子类,存在以下属性:
- position : Offset,指针相对于全局坐标的偏移
- delta : Offset,两次指针移动事件的距离
- orientation : double,检测到的物体的方向(指针移动方向),以弧度为单位
3,组件嵌套
组件嵌套时,事件传递机制如何控制?
通过两个组件: IgnorePointer 和 AbsorbPointer
- IgnorePointer :此节点和其子节点,都将忽略点击事件,使用ignoring 区分是否忽略(true)
- AbsorbPointer : 此节点响应点击事件,但阻止事件传递到子节点
4,命中测试
当手指按下、移动、抬起时,flutter 会给每个事件新建一个对象,PointerDownEvent、PointerMoveEvent 、PointerUpEvent
对每个事件对象,Flutter 都会执行命中测试:
(1)从最底层widget 开始执行命中测试。 是否命中看 hitTestChildren() 或 hitTestSelf()
(2)从下往上,直到找到第一个命中Widget,将它加入命中列表。 同时,将其父Widget也加入命中列表。
5,事件处理流程
- 命中测试:当手指按下时,触发 PointerDownEvent 事件,按照深度优先遍历当前渲染(render object)树,对每一个渲染对象进行“命中测试”(hit test),如果命中测试通过,则该渲染对象会被添加到一个 HitTestResult 列表当中。
- 事件分发:命中测试完毕后,会遍历 HitTestResult 列表,调用每一个渲染对象的事件处理方法(handleEvent)来处理 PointerDownEvent 事件,该过程称为“事件分发”(event dispatch)。随后当手指移动时,便会分发 PointerMoveEvent 事件。
- 事件清理:当手指抬( PointerUpEvent )起或事件取消时(PointerCancelEvent),会先对相应的事件进行分发,分发完毕后会清空 HitTestResult 列表
二、手势操作监听 GestureDetector组件
父组件GestureDetector,可以对其子组件进行手势监听
1,常用属性
支持点击(Tap)、双击(Double Tap)、长按(Long Press)、拖动(Drag)、缩放(Scale)、压力感应(Force Press)等20+种手势事件,




路由管理
一、定义
1,静态路由:明确知道跳转界面。直接注册,不能传递参数。
(1)MaterialApp 组件的 routes 定义路由表Map
runApp(
MaterialApp(
title: '一个Flutter应用', home: HomePage(),
routes: {
'/home': (BuildContext context) => HomePage(),
'/detail': (BuildContext context) => DetailPage()
},
onUnknownRoute: (setting) {
return MaterialPageRoute(builder: (_) => Home());
},//路由出错,找不到界面
home: Page1(title: "主页面",),//主界面
))
(2)路由跳转 - 跳转静态路由
onPressed:(){
Navigator.pushNamed( context, '/home' )
}
(3)返回
也就是出栈
Navigator.pop(context)
(4)也可以传参
'/detail': (BuildContext context) => DetailPage(arguments: arguments)
2,动态路由,动态携带参数跳转
Navigator.push(context,MaterialPageRoute(builder: (context){
return new SecondPage(title: "SecondPage");
}); )
3,参数回传
A -> B , B 返回时,传值A
//A -> B
Navigator.push(
context,
MaterialPageRoute(
// 传递title为SecondPage,跳转到第二个界面就会把标题设置为SecondPage
builder: (context) => SecondPage(title: "SecondPage"),
// 调用then等待接收返回数据
)).then((value) => print(value));
//B -> A
Navigator.pop(context, "返回传递数据");
4,路由栈对应
(1)Navigator.push 和 Navigator.pop 对应 standard 启动模式。
(2)Navigator.of(context).pushReplacementNamed(),替换
(3)Navigator.of(context).pushNamedAndMoveUntil(),清空路由栈以上
(3)Navigator.popUtil( context, ModalRoute.withNage('/page2')),清空路由栈,直到page2,并直接返回page2
三方库 fluro
网络
http 库、dio库
单例模式、拦截器、适配器
json解析
1,手动解析:(dart:convert )
/*将字符串转成json 返回的是键值对的形式*/
Map<String, dynamic> news = jsonDecode(jsonData);
String sats = news['result']['stat'];
2,自动解析
自动生成 bean 文件后
/*先将字符串转成json*/
Map<String, dynamic> json = jsonDecode(jsonData);
/*将Json转成实体类*/
NewsBean newsBean=NewsBean.fromJson(news);
/*取值*/
String sats = newsBean.result.stat;
@JsonKey 注解
三、异步
数据存储
一、SharedPreferences
1,和安卓的区别:
(1)都是存储键值对,安卓是同步的,flutter 是异步的
(2)运行在安卓上的应用基于SharedPreferences,运行ios上基于 NSUserDefaults 开发
2,使用
(1)导库 yaml
shared_preferences: ^0.5.7+3
(2)基本操作
SharedPreferences prefs = await SharedPreferences.getInstance();
//存
await prefs.setString('username', 'FlutterDev');
//取
String? username = prefs.getString('username');
//删
await prefs.remove('username');
// 清空
await prefs.clear();
SQLite
文件存储 path_provider
path_provider可以创建和管理任意类型的文件,包括文本、图片、音频、视频等
1,App 存储目录类型
(1)临时目录
临时目录是系统可以随时清空的缓存文件夹
- iOS对应的实现方式是 NSCachesDirectory
- Android对应的实现方式是getCacheDir()
(2)文档目录
文档目录用于存储只能由该应用访问的文件,系统不会清除该目录,只有在删除应用时才会消失。
- iOS对应的实现方式是 NSDocumentDirectory
- Android对应的实现方式是 AppData
(3)应用程序支持目录
应用程序支持目录用于不想向用户公开的文件,也就是你不想给用户看到的文件可放置在该目录中,系统不会清除该目录,只有在删除应用时才会消失。
- iOS对应的实现方式是 NSApplicationSupportDirectory
- Android对应的实现方式是 getFilesDir()
(4)应用程序持久文件目录
该目录主要存储持久文件的目录,并且不会对用户公开,常用于存储数据库文件,比如sqlite.db等。
(5)外部存储目录
主要用于获取外部存储目录,如SD卡等,但iOS不支持外部存储目录,目前只有Android才支持。
(6)外部存储缓存目录
主要用户获取应用程序特定外部缓存数据的目录,比如从SD卡或者手机上有多个存储目录的,但iOS不支持外部存储目录,目前只有Android才支持。
(7)外部存储目录(单独分区)
可根据类型获取外部存储目录,如SD卡、单独分区等,和外部存储目录不同在于他是获取一个目录数组。但iOS不支持外部存储目录,目前只有Android才支持。
2,path_provider 提供的方法和说明
