});
我们可以看看 mounted
什么是源码?
BuildContext get context => _element; StatefulElement _element;
/// Whether this [State] object is currently in a tree. /// /// After creating a [State] object and before calling [initState], the /// framework “mounts” the [State] object by associating it with a /// [BuildContext]. The [State] object remains mounted until the framework /// calls [dispose], after which time the framework will never ask the [State] /// object to [build] again. /// /// It is an error to call [setState] unless [mounted] is true. bool get mounted => _element != null;
BuildContext
是Element
你可以认为抽象mounted
就是 context
是否存在。然后也用于回调。 context
还需要判断mounted
。例如,我们必须弹出一个 Dialog
当请求界面成功时,或退出当前页面。BuildContext
概念更重要,需要掌握,错误使用一般不会崩溃,但会使代码无效。
详细代码见:[点击查看]()
2.监听Dialog的关闭
问题描述:我会在每个接口请求之前弹出一个Dialog
做loading提示,当接口请求成功或失败时关闭。但是,如果我们点击返回键人工关闭请求,当请求成功或失败时,因为我们调用它Navigator.pop(context)
我们错误地关闭了当前的页面。
那么解决问题的突破口就是什么时候知道Dialog关闭可以使用 WillPopScope
同时记录返回键的输入Dialog的关闭。
bool _isShowDialog = false;
void closeDialog() { if (mounted && _isShowDialog){ _isShowDialog = false; Navigator.pop(context); } }
void showDialog() { /// 避免重复弹出 if (mounted && !_isShowDialog){ isShowDialog = true; showDialog( context: context, barrierDismissible: false, builder:() { return WillPopScope( onWillPop: () async { // 拦截返回键,证明dialog被手动关闭 _isShowDialog = false; return Future.value(true); }, child: ProgressDialog(hintText: “正在加载…”), ); } ); } }
详细代码见:[点击查看]()
3.addPostFrameCallback
addPostFrameCallback
回调方法在Widget渲染完成时触发,所以我们通常在获取页面Widget使用大小和位置。
第二点,我说我会在接口请求前弹出loading。如果我把请求方法放在一边,initState
异常方法如下:
inheritFromWidgetOfExactType(_InheritedTheme) or inheritFromElement() was called before initState() completed. When an inherited widget changes, for example if the value of Theme.of() changes, its dependent widgets are rebuilt. If the dependent widget’s reference to the inherited widget is in a constructor or an initState() method, then the rebuilt dependent widget will not reflect the changes in the inherited widget. Typically references to inherited widgets should occur in widget build() methods. Alternatively, initialization based on inherited widgets can be placed in the didChangeDependencies method, which is called after initState and whenever the dependencies change thereafter.
原因:弹出一个DIalog的showDialog
方法会调用Theme.of(context, shadowThemeOnly: true)
,这种方法将通过inheritFromWidgetOfExactType
跨组件获取Theme对象。
[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-0l92425P-1651125955287)(https://user-gold-cdn.xitu.io/2019/ 7/11/16bdea7aac8f472a?imageView2/0/w/1280/h/960/ignore-error/1)]
inheritFromWidgetOfExactType
方法调用inheritFromElement
:
[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-KHFsE9Kr-1651125955288)(https://user-gold-cdn.xitu.io/2019/7/11/16bdea7aac93bfa9?imageView2/0/w/1280/h/960/ignore-error/1)]
但是在_StateLifecycle
为created
和 defunct
不能跨组件获取数据,即initState()
时和dispose()
之后。所以错误的信息提示我们 didChangeDependencies
调用。
然而放在didChangeDependencies
后来,新的异常:
setState() or markNeedsBuild() called during build. This Overlay widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
提示页面在build时不能调用setState()
或 markNeedsBuild()
方法。所以我们需要build只有完成后,才能创建这个新组件(这里是Dialog)。
所以解决方就是使用addPostFrameCallback
回调方法,等待页面build完成后在请求数据:
@override void initState() { WidgetsBinding.instance.addPostFrameCallback((_){ /// 接口请求 }); }
导致这类问题的场景很多,但是大体解决思路就是上述的办法。
本问题详细的代码见:[点击查看](()
4.删除emoji
不多哔哔,直接看图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FMFadCUV-1651125955289)(https://user-gold-cdn.xitu.io/2019/7/11/16bdea7aaca2db4b?imageslim)]
简单说就是删除一个emoji表情,一般需要点击删除两次。碰到个别的emoji,需要删除11次!!其实这问题,也别吐槽Flutter,基本emoji在各个平台上都或多或少有点问题。
原因就是:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0U65gzZ1-1651125955289)(https://user-gold-cdn.xitu.io/2019/7/11/16bdea7aea106e0f?imageView2/0/w/1280/h/960/ignore-error/1)]
这个问题我发现在Flutter 的1.5.4+hotfix.2
版本,解决方法可以参考:[github.com/flutter/eng…](() 虽然只适用于长度为2位的emoji。
幸运的是在最新的稳定版1.7.8+hotfix.3
中修复了这个问题。不幸的是我发现了其他的问题,比如在我小米MIX 2s上删除文字时,有时会程序崩溃,其他一些机型正常。异常如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k0B2wmeK-1651125955289)(https://user-gold-cdn.xitu.io/2019/7/11/16bdea7aafff3c77?imageView2/0/w/1280/h/960/ignore-error/1)]
我也在Flutter上发现了同样的问题Issue,具体情况可以关注这个Issue :[github.com/flutter/flu…](() ,据Flutter团队的人员的回复,这个问题修复后不太可能进入1.7的稳定版本。。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZhkXIKvL-1651125955290)(https://user-gold-cdn.xitu.io/2019/7/11/16bdea7ac4c65beb?imageView2/0/w/1280/h/960/ignore-error/1)]
所以建议大家谨慎升级,尤其是用于生产环境。那么这个问题暂时只能搁置下来了,等待更稳定的版本。。。
,官方发布了1.7.8+hotfix.4
,修复了此问题。经过测试问题修复,大家可以放心使用了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HtRV60p8-1651125955290)(https://user-gold-cdn.xitu.io/2019/7/20/16c0d901dc5e9450?imageView2/0/w/1280/h/960/ignore-error/1)]
5.键盘
1.是否弹起
MediaQuery.of(context).viewInsets.bottom > 0
viewInsets.bottom
就是键盘的顶部距离底部的高度,也就是弹起的键盘高度。如果你想实时过去键盘的弹出状态,配合使用didChangeMetrics
。完整如下:
import ‘package:flutter/material.dart’;
typedef KeyboardShowCallback = void Function(bool isKeyboardShowing);
class KeyboardDetector extends StatefulWidget {
KeyboardShowCallback keyboardShowCallback;
Widget content;
KeyboardDetector({this.keyboardShowCallback, @required this.content});
@override _KeyboardDetectorState createState() => _KeyboardDetectorState(); }
class _KeyboardDetectorState extends State with WidgetsBindingObserver { @override void initState() { WidgetsBinding.instance.addObserver(this); super.initState(); }
@override void didChangeMetrics() { super.didChangeMetrics(); WidgetsBinding.instance.addPostFrameCallback((_) { print(MediaQuery.of(context).viewInsets.bottom); setState(() { widget.keyboardShowCallback ?.call(MediaQuery.of(context).viewInsets.bottom > 0); }); }); }
@override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); }
@override Widget build(BuildContext context) { return widget.content; } }
代码来自项目GSYFlutterDemo:https://github.com/CarGuo/GSYFlutterDemo
nce.removeObserver(this); super.dispose(); }
@override Widget build(BuildContext context) { return widget.content; } }
代码来自项目GSYFlutterDemo:https://github.com/CarGuo/GSYFlutterDemo