che*_*ser 6 architecture dart flutter
我构建 Flutter 应用程序 + Dart。现在我试图在一个地方(类) 并显示所有未来的异常AlertDialog。
Flutter Docs 提出了 3 种捕获async错误的解决方案:
但没有人能够实现所有目标(以最纯粹的形式):
第一:无法在小部件中运行build(需要返回Widget,但返回Widget?.
第二:在 中工作build,但不要捕获异步错误,这些错误是由未等待的 futures 引发的,并且是“脏”的(强制使用WidgetBinding.instance.addPostFrameCallback。我可以确保等待 futures (这增加了麻烦),但我无法检查是否确保它是第三方库。因此,这是坏情况。
第三:与第二类似。而且看起来很可怕。
我得到了第一个解决方案并添加了一些细节。所以,
我创建了ZonedCatcher,它显示AlertDialog异常或累积异常,如果它不知道在哪里显示AlertDialog(BuildContext尚未提供)。
AlertDialog要求MaterialLocalizations,所以BuildContext是从MaterialApp孩子身上拿走的MaterialChild。
void main() {
ZonedCatcher().runZonedApp();
}
...
class ZonedCatcher {
BuildContext? _materialContext;
set materialContext(BuildContext context) {
_materialContext = context;
if (_exceptionsStack.isNotEmpty) _showStacked();
}
final List<Object> _exceptionsStack = [];
void runZonedApp() {
runZonedGuarded<void>(
() => runApp(
Application(
MaterialChild(this),
),
),
_onError,
);
}
void _onError(Object exception, _) {
if (_materialContext == null) {
_exceptionsStack.add(exception);
} else {
_showException(exception);
}
}
void _showException(Object exception) {
print(exception);
showDialog(
context: _materialContext!,
builder: (newContext) => ExceptionAlertDialog(newContext),
);
}
void _showStacked() {
for (var exception in _exceptionsStack) {
_showException(exception);
}
}
}
...
class MaterialChild extends StatelessWidget {
MaterialChild(this.zonedCatcher);
final ZonedCatcher zonedCatcher;
@override
Widget build(BuildContext context) {
zonedCatcher.materialContext = context; //!
...
}
}
Run Code Online (Sandbox Code Playgroud)
materialContext只能从子级获取MaterialApp,但页面已在小部件中设置MaterialApp。也许,我会注入ZonedCatcher所有页面,并且构建页面将重新设置materialContext。但我可能会遇到GlobalKey的问题,比如materialContext在手势上同时重置某些页面。默认情况下,如果 Flutter 应用程序中存在未捕获的异常,则会将其传递给FlutterError.onError. 这可以用 a 覆盖void Function(FlutterErrorDetails)以提供自定义错误处理行为:
void main() {
WidgetsFlutterBinding.ensureInitialized();
FlutterError.onError = (details) {
print(details.exception); // the uncaught exception
print(details.stack) // the stack trace at the time
}
runApp(MyApp());
}
Run Code Online (Sandbox Code Playgroud)
如果您想在此代码中显示一个对话框,您将需要访问 a BuildContext(或一些等效的机制来挂钩到元素树)。
执行此操作的标准方法是使用GlobalKey. 在这里,为了方便起见(因为您想显示一个对话框),您可以使用GlobalKey<NavigatorState>:
void main() {
WidgetsFlutterBinding.ensureInitialized();
final navigator = GlobalKey<NavigatorState>();
FlutterError.onError = (details) {
navigator.currentState!.push(MaterialPageRoute(builder: (context) {
// standard build method, return your dialog widget
return SimpleDialog(children: [Text(details.exception.toString())]);
}));
}
runApp(MyApp());
}
Run Code Online (Sandbox Code Playgroud)
请注意,如果您需要BuildContext内部onError回调,您也可以使用navigator.currentContext!.
然后您需要将您的传递给GlobalKey<NavigatorState>(MaterialApp或者Navigator如果您手动创建它):
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: navigatorKey, // pass in your navigator key
// other fields
);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3432 次 |
| 最近记录: |