1.Flutter使用InkWell无水波纹效果
2.Flutter 分类网站推荐
3.tabbar设置点击的背景颜色
Container(
color: Colors.white,
child: Stack(
children: [
Positioned(
left: MediaQuery.of(context).size.width / 3,
child: Container(
width: 2,
height: 42,
color: Colors.grey[200],
),
),
Positioned(
left: MediaQuery.of(context).size.width / 3 * 2,
child: Container(
width: 2,
height: 42,
color: Colors.grey[200],
),
),
TabBar(
labelStyle:
TextStyle(color: Colors.white, fontSize: AppFont.f14),
unselectedLabelColor: Colors.black,
unselectedLabelStyle:
TextStyle(color: Colors.grey, fontSize: AppFont.f14),
indicator: BoxDecoration(
color: Global()
.appConfig
.colorTheme
.pressStateStartButtonBackground),
controller: viewModel.tabController,
tabs: viewModel.items
.map((e) => e.itemBuilder(context))
.toList(),
),
],
),
),
4.Flutter Column嵌套 Row Text 超出屏幕
https://www.codeleading.com/article/24305929369/
说明:当这个Text超出屏幕时,不只是他自己控件上显示黄色警告,在它父布局上也有,说明父布局超出了,就应该修改父布局的显示问题
5.了解Flutter Sliver 组件
6.Dart判断数组是否全选方法
/// 是否全选
bool get isAllSelected => fields.every((field) => field.isSelected);
7.设置项目全局context
在根页面中设置全局的context
获取全局的 globalKey.currentState!.context
参考toast的使用 https://github.com/ponnamkarthik/FlutterToast
import 'package:flutter/material.dart';
GlobalKey globalKey = GlobalKey();
void main() => runApp(
MaterialApp(
home: MyApp(),
),
);
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
key: globalKey,
);
}
}
8.解决 Flutter showModalBottomSheet 被弹出的键盘、输入法遮挡
在使用 showModalBottomSheet 底部弹出内容时,
如果唤起了键盘、输入法,showModalBottomSheet 中的内容往往会被挡住,
给 showModalBottomSheet 设置 isScrollControlled
再套一个 SingleChildScrollView 以及 设定一个 padding 一般就能解决问题了。
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) {
return SingleChildScrollView(
child: Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
OrderAllocationBottomView(
model: OrderAllocationModel(
color: 'A02',
styleList: [],
styleName: '服装服饰',
userName: '华中区',
),
),
],
),
),
);
},
);
9.碰到点击顶部区域无法响应点击事件
原因在于点击区域在安全区域状态栏里面
所以点击没有反应,在安全区域进行点击即可
10.Flutter的Flexible和 Expanded的区别
Flexible与Expanded的相同点是都必须使用在Row、Column、Flex内部
https://www.jianshu.com/p/957f868e45ab
11.对于StatefulWidget怎么更新数据源刷新页面
第一种通过 ChangeNotifier 传入一个Controller 控制
第二种通过 didUpdateWidget 对比数据源进行更新 第二种比较常用 比较推荐
12.Flutter动态的获取widget宽高
获取某个控件的宽高,可以通过设置GlobalKey来实现。
final GlobalKey globalKey = GlobalKey();
Container(
key: globalKey,
)
注意,需要等widget布局完成之后才能获取宽高
var width = globalKey.currentContext?.size?.width ?? 0;
var height = globalKey.currentContext?.size?.height ?? 0;
13.hitTest:自绘组件点击事件的处理
自绘组件点击事件的处理(尤其是外层使用Stack时更需要注意)
首先需要重写hitTest()函数
默认情况下返回null,事件不会向下传递,也不会进行处理
如果返回true则当前组件进行处理事件
如果返回false则当前组件不会响应点击事件,会向下一层传递
@override
bool? hitTest(Offset offset) => null;
如果需要处理点击事件,也可通过此方法进行处理,并可通过offset确认点击的位置,比如:
@override
bool? hitTest(Offset offset) {
for (int i = 0; i < paths.length; i++) {
if (paths[i].contains(offset) && !holePath.contains(offset)&&tapIndex!=i) {
onTap?.call(i);
tapIndex = i;
return true;
}
}
return false;
}
14.计算文本的宽高
计算文本的宽高可以通过TextPainter来计算
extension TextSizeExtension on Text {
Size calculateTextSize(
BuildContext context,
) {
if (data == null || (data?.isEmpty ?? true)) {
return Size.zero;
}
TextPainter painter = TextPainter(
locale: Localizations.localeOf(context),
maxLines: maxLines,
textDirection: TextDirection.ltr,
text: TextSpan(
text: data,
style: style,
),
);
painter.layout();
return painter.size;
}
}
使用
var textSize = Text("text").calculateTextSize(context);
15.IgnorePointer组件
IgnorePointer可以拦截触摸事件,让其子Widget不再响应相关事件。
比如,我需要禁止ListView滑动
IgnorePointer(
child: ListView.builder(
shrinkWrap: true,
itemCount: value.length,
itemBuilder: (context, index) {
return Container();
},
),
)
原理
有一个默认参数ignoring为true,最终作用于hitTest中判断是否拦截此事件
16.LayoutBuilder组件:在build的时候获取当前Widget的大小
如果需要根据当前组件的大小进行布局便可使用此组件,constraints.maxHeight为组件的高度,constraints.maxWidth为组件的宽度
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
var color = Colors.red;
if (constraints.maxHeight > 100) {
color = Colors.blue;
}
return Container(
height: 50,
width: 50,
color: color,
);
},
);
}
17.Dart 数组reduce
Dart数组的reduce可用指定的函数方式对数组中的所有元素做连续操作,并将结果返回,比如可以用于计算数组中的最大值,最小值,所有元素的和。
例:
1.数组中的最大值:
import 'dart:math';
var maxValue = lists.reduce(max)
/// 或者
var maxValue = values.reduce((v, e) => max(v, e))
2.数组中的最小值:
import 'dart:math';
var minValue = lists.reduce(min)
/// 或者
var minValue = values.reduce((v, e) => min(v, e))
3.数组中的所有元素的和
var sum = lists.reduce((v, e) => v + e)
18.Android 运行报错:App requires Multidex support
D8: Cannot fit requested classes in a single dex file (# methods: 67509 > 65536)
com.android.builder.dexing.DexArchiveMergerException: Error while merging dex archives:
The number of method references in a .dex file cannot exceed 64K.
Learn how to resolve this issue at https://developer.android.com/tools/building/multidex.html
......
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:mergeDexDebug'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade
> com.android.builder.dexing.DexArchiveMergerException: Error while merging dex archives:
The number of method references in a .dex file cannot exceed 64K.
Learn how to resolve this issue at https://developer.android.com/tools/building/multidex.html
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 52s
[!] App requires Multidex support
┌─ Flutter Fix ──────────────────────────────────────────────────────────────────────────────────┐
│ Flutter multidex handling is disabled. If you wish to let the tool configure multidex, use the │
│ --mutidex flag. │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
Exception: Gradle task assembleDebug failed with exit code 1
解决
修改模块级 build.gradle 文件以启用 MultiDex,并将 MultiDex 库添加为依赖项,如下所示
android {
defaultConfig {
...
multiDexEnabled true
}
...
}
dependencies {
implementation "androidx.multidex:multidex:2.0.1"
}
19.当TextField被Container包裹时如何使输入的文字上下居中
https://blog.csdn.net/qq1377399077/article/details/108852342
一般该场景我们来给输入框设置一个背景色,
但是有时候会发现输入框光标及内容并不居中(其实另一种InputDecoration也可以设置输入框的背景)
20.网络请求动态显示tab标签,设置TabController
https://blog.csdn.net/wayne214/article/details/104002718
在创建tabbar和tabview后,进行网络请求后显示顶部tab标签,
设置TabController,并使class类实现SingleTickerProviderStateMixin,
但是会报错“A SingleTickerProviderStateMixin can only be used as a TickerProvider once”,
主要是因为多个地方调用setState请求重绘,
但是state使用的是SingleTickerProviderStateMixin ,
将其改成TickerProviderStateMixin即可
21.Flutter 底部弹窗showModalBottomSheet如何调整高度
https://blog.csdn.net/Dnnis/article/details/116798633
弹窗默认为屏幕的二分之一无法改变高度,
但是将isScrollControlled方法设置为true,
然后重新设置一下builder里面的布局高度就可解决
22.tabbar设置每个item的宽度需要设置isScrollable为true
https://api.flutter.dev/flutter/material/TabBar/isScrollable.html
isScrollable设置为true后,可以自定义设置每个item的宽度
23.类似原生的生命周期
24.如何居中SingleChildScrollView使背景拉伸以填充屏幕?
https://www.thinbug.com/q/56497666
尝试下面这种方法可以
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: ConstrainedBox(
//Use MediaQuery.of(context).size.height for max Height
constraints: BoxConstraints(minHeight: MediaQuery.of(context).size.height),
child: Center(
child: //Widget,
),
),
);
25.怎么将👀
转换为表情?
网上找了很多文章,都没有关于这方面的说法。
今天无意间看到官方初始化String的方法,测试竟然成功了
,目前没保证这个方法覆盖所有的表情,理论上是可以的。
直接用谷歌搜索👀,会直接变成表情。导致搜出出来,更换搜索关键词为👀
![image.png](https://upload-images.jianshu.io/upload_images/2356427-22b2966ba7440bb6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
搜出来关键词Unicode Decimal Code,但是这个关键词也是没找到原因。
下面发现去掉去掉只用中间的数字解决。
/// 解析消息内容的表情
/// [content] 消息内容
static String parseEmoji(String content) {
/// 正则
final reg = RegExp(r'[0-9]*;');
final matchs = reg.allMatches(content);
if (matchs.isEmpty) {
return content;
}
for (var match in matchs) {
final emojiCodeContent = match.group(0);
if (emojiCodeContent == null) continue;
final emojiCode =
emojiCodeContent.replaceAll('', '').replaceAll(';', '');
final emoji = String.fromCharCodes([int.parse(emojiCode)]);
content = content.replaceAll(emojiCodeContent, emoji);
}
return content;
}
26.怎么 ListView 更新数据之后自动滑动到底部
_imController.scrollController.animateTo(
_imController.scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 300),
curve: Curves.easeOut,
);
延时16毫秒之后 更新UI 因为60帧渲染师是16毫秒 但是卡顿就不好说了
最好是200-500毫秒
监听渲染完毕 注意下面的代码不能写在initState里面,
请写在build方法里面,保证每次更新都重新注册。
WidgetsBinding.instance?.addPostFrameCallback((_) {
/// do something
});
监听每一帧的回掉
WidgetsBinding.instance?.addPersistentFrameCallback((timeStamp) {
/// 监听是否需要滚动到底部
if (_imController.isScrollToBottom) {
_imController.isScrollToBottom = false;
_imController.scrollController.animateTo(
_imController.scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 300),
curve: Curves.easeOut,
);
}
});
27.flutter APP中禁止软键盘弹出,监听硬键盘输入方法
https://chowdera.com/2022/02/202202260549433593.html
禁止软键盘弹出只需要设置showCursor为true, readOnly为true,
可以聚焦但是不会弹出键盘。如果想要弹出键盘,
目前能想到的方法是设置一个bool值给readOnly属性,通过刷新页面的方式修改
TextFormField(
controller: _controller,
showCursor: true, // 显示光标
readOnly: true, // 设置只读,点击输入框时便不会弹出软键盘
onEditingComplete: () {
onOperateLot('Add');
},
validator: (String value) {
if (value == null || value.isEmpty) {
return '该项为必填项';
}
return null;
},
)
28.实现一个类像方法一样掉用
29.DefaultTextStyle 可以统一默认子元素的样式
DefaultTextStyle(
style: const TextStyle(
fontSize: 28,
color: Colors.white,
fontWeight: FontWeight.bold,
),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text("欢迎登录 App 周报管理系统"),
_buildInputContent(context: context),
],
),
),
);
30.Flutter TextField的onEditingComplete与onSubmitted的区别
onEditingComplete回调无参数
onSubmitted回调是当前输入内容
onEditingComplete与onSubmitted都在同一个方法内调用.先调用onEditingComplete再调用onSubmitted.
有onEditingComplete方法时,会忽略设置了的TextInputAction类型系统默认焦点操作.(释放焦点,跳转下一个/上一个焦点)
一般情况下,只获取输入结果的实现onSubmitted
若要输入完成后,不让系统自动控制焦点跳转,实现onEditingComplete
看下源码
31.Flutter 监听页面跳转实现类似android的onResume onPause,ios的viewWillDisappear viewDidDisappear
https://blog.csdn.net/qq_20352713/article/details/125187928
通过RouteAware监听路由事件,通过WidgetsBindingObserver监听app状态.二者结合即可
32.Flutter TabView设置indicator宽度
https://blog.csdn.net/xudailong_blog/article/details/98965866
正常情况下,我们可以设置Tabbar的indicatorSize来设置indicator的宽度,
但是默认每个tabbar会平分,造成indicator也仅仅是有一定的距离来达到缩短的效果,
这个时候就需要重写indicator继承Decoration来实现对indicator宽度的修改。
33.Dart 解包转换操作
1 使用系统方法
String? name = "king";
name.runes.map((e) => 1).map((e) => e + 10).map((e) => [2]).first; /// [2]
2 使用 darty_json_safe 库
Unwrap(name).map((e) => 1).map((e) => e + 10).map((e) => [2]).value;
34.怎么实现两个滚动试图联动
_leftScrollController.addListener(() {
final _left = _leftScrollController.offset;
final _right = _rightScrollController.offset;
/// 为了自动更新左侧 导致右侧继续更新
if (_left == _right) return;
_rightScrollController.jumpTo(_leftScrollController.offset);
});
_rightScrollController.addListener(() {
final _left = _leftScrollController.offset;
final _right = _rightScrollController.offset;
/// 为了自动更新右侧 导致左侧继续更新
if (_left == _right) return;
_leftScrollController.jumpTo(_rightScrollController.offset);
});
35.Flutter 基于Intl插件实现app国际化
https://blog.csdn.net/qq_20352713/article/details/125259871?spm=1001.2014.3001.5501
含插件使用.及app内切换语言
36.dart多线程(Isolate,compute,LoadBalancer的使用)
isolate
//使用isolate方法计算
createIsolateCountEven(int num) async {
//1.创建当前isolateA的接收端口
var aReceived = ReceivePort();
staticVar = 3;
//2.创建新的isolateB,并且把当前A的端口带过去
var newIsolate =
await Isolate.spawn(createNewIsolateCountEven, aReceived.sendPort);
aReceived.listen((message) {
if (message is SendPort) {
//5.拿到发送端口可以发送消息了
message.send(num);
} else {
_count = message;
setState(() {});
//不需要时 释放isolate
newIsolate.kill();
}
});
}
//运行在isolateB的方法
static void createNewIsolateCountEven(SendPort aPort) {
//3.创建isolateB的端口
var bReceived = ReceivePort();
//4.把isolateB的的发送端口带过去
aPort.send(bReceived.sendPort);
bReceived.listen((message) {
//6.监听到消息可以做处理了
//调用static方法或者top-level方法
var res = topCountEven(message);
aPort.send(res);
});
}
//compute
Future computeCountEven(int num) async {
///compute 使用的必须是顶层函数或者是static函数
return await compute(topCountEven, num);
}
//LoadBalancer
//1.找个地方创建[LoadBalancer]
Future loadBalancer =
LoadBalancer.create(2, IsolateRunner.spawn);
Future loadBalancerCountEven(int num) async {
//2.获取loadBalancer
var lb = await loadBalancer;
//3.传入函数,参数
//run的泛型为<函数的返回值类型,参数类型>
var res = await lb.run(topCountEven, num);
return res;
}
isolate手动控制
compute一次性使用
LoadBalancer相当于线程池
37.Flutter 取两位小数 不要四舍五入的方法
https://www.jianshu.com/p/adfabf566335
使用toStringAsFixed()方法
38.解决键盘弹起遮挡showDialog
showDialog(
context: context,
builder: (BuildContext context) {
return Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: Container(),
);
});
39.Mac 必备开源 app 的集合
40.Flutter 设置屏幕旋转方向
开发过程中,移动设备为了避免屏幕自动旋转而带来一些不好的显示效果(未适配的前提下),通常我们会将设备的方向定死始终竖屏,就需要用到下面这个方法:
SystemChrome.setPreferredOrientations([]);
可以写在App入口main方法里面或者某个widget的initState里面,方法参数是DeviceOrientation数组类型,系统为我们提供了四种DeviceOrientation,
portraitUp:固定的常规的竖屏显示方式;
portraitDown:固定的反方向的竖屏显示方式(倒置手机,App头部在手机底部);
landscapeLeft:固定的横屏显示方式(App头部显示在手机左侧);
landscapeRight:固定的横屏显示方式(App头部显示在手机左侧);
传空数据/四个类型都传:则四个方向都可以旋转显示(前提是开启自动旋转)
一般来说将该方法设置在main方法里面使整个app起作用,但可能需要某个页面单独配置则在ininState方法里面传入你所需要的方向即可
41.SliverPersistentHeaderDelegate 中build 方法参数 BuildContext 无法获取外部页面的 Provider 对象
目前解决办法,使用 SliverPersistentHeaderDelegate 外部的 BuildContext 获取 Provider 对象
原因在于 context.select() 只允许在build方法内部或者在LayoutBuilder方法内部,在SliverPersistentHeaderDelegate的内部就报错了
42.dart导入内置包(使用内部math中取最大值时遇到的问题)
1.导入内置包
Dart语言内置了一些常用的包,这些内置包随着Dart sdk一起安装在本地。导入内置包使用 dart: 作为路径前缀。
例子
// 导入内置math包,使用dart:作为前缀。
// math包主要提供一些数学相关的函数,例如,正弦函数、求最大值函数等等
import 'dart:math';
void main() {
// 调用math包中的max函数,求两个数中的最大值。
var a = max(1,100);
print(a);
}
2.包的别名
通过1的例子我们知道,默认情况调用包中的函数或者类,不需要包名作为前缀,上面调用了math包中的max函数,直接使用包中的函数名。但是这样会存在命名冲突的可能性,如果导入的两个包,包含了同名的类或者函数,就会出现命名冲突,因此提供别名机制。
别名例子:
// 使用 as 关键词,指定包的别名
import 'dart:math' as math;
void main() {
// 使用别名,引用包中的函数 。
var a = math.max(1,100);
print(a);
}
提示: 推荐使用别名引用包的成员,不仅可以解决命名冲突问题,也可以增加代码的可读性,我们可以直观知道到底引用的是那个包的成员。
3.导入包的部分内容。
有时候我们不想导入整个包,只想导入包里面的某个类或者某个函数。dart提供了show和hide关键词处理导入包的部分内容。
例子:
// 仅导入max函数, 导入多个内容使用逗号分隔,例如 show max,sin
import 'dart:math' show max;
// 除了max函数,导入math中的所有内容。
import 'dart:math' hide max;
43.Flutter json_serializable库解析泛型最佳方案
将JsonSerializable的genericArgumentFactories参数设置为true
修改fromJson,toJson
@JsonSerializable(genericArgumentFactories: true)
class BaseJson {
int? code;
String? msg;
T? data;
BaseJson();
factory BaseJson.fromJson(
Map json, T Function(dynamic json) fromJsonT) =>
_$BaseJsonFromJson(json, fromJsonT);
Map toJson(
Object? Function(T value) toJsonT,
) =>
_$BaseJsonToJson(this, toJsonT);
}
使用
var jsonData =
"{\"code\": 200001,\"msg\": \"success\",\"data\": {\"mydata\": {\"name\": \"张三\",\"age\": \"21\",\"tel\": \"1539324****\",\"balance\": \"100\"}}}";
//JSON字符串解析成map
var jsonMap = json.decoder.convert(jsonData);
//map转model
BaseJson> baseJson = BaseJson.fromJson(jsonMap, (json) {
//多层泛型就这样多层解析
return Data1.fromJson(json, (json) {
return MyData.fromJson(json);
});
});
//model转jsonMap
print(baseJson.toJson((value) => value.toJson((value) => value.toJson())));
data为List时
var jsonData =
"{\"code\": 200001,\"msg\": \"success\",\"data\": [{\"name\":\"张三\",\"age\": \"21\",\"tel\": \"1539324****\",\"balance\": \"100\"}]}";
//json字符串转jsonMap
var jsonMap = json.decoder.convert(jsonData);
//jsonMap转model
BaseJson> baseJson = BaseJson.fromJson(jsonMap, (json) {
//判断json是不是数组类型
if (json is List) {
//map操作符遍历生产model
return json.map((e) => MyData.fromJson(e)).toList();
}
//其它情况返回个空数组
return [];
});
//model转jsonMap
print(baseJson.toJson((value) => value.map((value) => value.toJson()).toList()));
44.Waiting for another flutter command to release the startup lock
当执行flutter pub get时遇到 Waiting for another flutter command to release the startup lock
执行 killall -9 dart
https://stackoverflow.com/questions/51679269/waiting-for-another-flutter-command-to-release-the-startup-lock
45.flutter有用的小技巧
1.字符串可以进行加和乘,但是减和除不行 例如 String string = "a"; string*2 就是 aa
2.隐藏组件 Visibility 和 Offstage https://juejin.cn/post/6953483034305921037
3.多个请求依赖, 仅用于多个异步操作都完成后, 执行其它操作 Future.wait用法 https://www.jianshu.com/p/f1460037d008
46.Dart 格式化DateTime 为字符串
需要导入intl库
flutter pub add intl
DateFormat("yyyy-MM-dd").format(dateTime);
47.context.select 和 Selector的区别
LayoutBuild((context,_) {
final title = context.select((value) => value.title);
return Text(title);
})
Selector(select:(context,value) => value.title, builder:(context, value,child) {
return Text(value);
})
context.select会在当前的作用域进行更新
48.在initState中设置状态栏颜色不生效问题
在initState中直接设置状态栏颜色不生效,可使用WidgetsBinding.instance?.addPostFrameCallback在里面设置
WidgetsBinding.instance?.addPostFrameCallback((_) {
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
statusBarColor: AppColor.c209090,
systemNavigationBarDividerColor: AppColor.c209090,
),
);
});
49.isolate的使用
1.https://www.jianshu.com/p/4b2fc461f3a8
2.https://www.jianshu.com/p/a524620e4bb5 (包含 exit 和 send 的区别及用法)
3.https://juejin.cn/post/7048844082348556295
4.https://www.jianshu.com/p/e621bb554d4f
50.listview性能优化
https://juejin.cn/post/6940134891606507534
https://github.com/LianjiaTech/keframe
51.Dart Extends 无法自动引入
貌似是官方一个 Bug
可以通过一个空的类 导入所有扩展引入 其他地方通过这个引入这个空的类 调用即可
extends HelloString on String {
String hello() => "Hello $this";
}
final name = "king";
final helloName = name.hello(); /// 这个地方报
错 不会自动引入头文件
可以通过下面方式解决
/// 第一步 打出 HelloString 自动导入 HelloString 对应的文件
/// 此时再调用
name.hello(); /// 已经不会报错
52.监听键盘弹起和收回
https://juejin.cn/post/6960858835523207181
记得一定要销毁,不然再次进入页面会出问题
class YourTextFieldState extends State with WidgetsBindingObserver {
@override
void initState() {
super.initState();
/// 初始化
WidgetsBinding.instance.addObserver(this);
}
@override
void didChangeMetrics() {
super.didChangeMetrics();
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
if(MediaQuery.of(context).viewInsets.bottom==0){
/// 键盘收回
}else{
/// 键盘弹出
}
});
});
}
@override
void dispose() {
/// 销毁
super.dispose();
WidgetsBinding.instance.removeObserver(this);
}
}
53.快速遍历并修改数组导致崩溃的原理
54.mounted 的使用原则