有什么方法可以强类型地使用 Navigator?

问题描述

我有一个带有以下 MaterialButton 字段的 onpressed

onpressed: () async {
    final int intResult = await showMyDialog(context,provider.myArg) ?? 0;
    //...
}

这是 showMyDialog 函数

Future<int?> showMyDialog(BuildContext context,Object someArg) async {
  return showDialog<int>(
    context: context,builder: (BuildContext context) {
      return ChangeNotifierProvider<MyProvider>(
        create: (_) => MyProvider(someArg),child: MyDialog(),);
    },);
}

现在我看到的问题是在 MyDialog (a StatelessWidget) 中,我需要使用 Navigator.pop(...) 为等待的 intResult 返回一个值。但是,我似乎无法找到一种方法来强类型这些调用,因此很难确定不会发生运行时类型错误

我现在最好的方法是继承 StatelessWidget 并将 Navigator 函数包装在其中:

abstract class TypedStatelessWidget<T extends Object> extends StatelessWidget {
  void pop(BuildContext context,[T? result]) {
    Navigator.pop(context,result);
  }
}

然后在TypedStatelessWidget<int>中我们可以正常使用pop(context,0),而pop(context,'hi')会被编辑器标记为类型错误。这仍然没有链接对话框返回类型和导航器,但至少它避免了手动输入每个导航器调用


有没有更好的方法来实现这种强类型?

解决方法

您无需包装 Navigator.pop 方法,因为您可以键入其返回值。

Navigator.pop<int>(context,1);

我让您参考 pop method documentation 了解更多详情。

,

所以这就是我最终要做的。本质上,这允许将小部件类型和提供程序类型“绑定在一起”。这样,当在对话框中调用自定义 pop 函数时,对于返回值应该是什么,有一个类型约定(参见 PopType)。还通过使用 WillPopScope 支持非手动弹出导航,它获取类型化提供程序的返回函数和参数(请参阅 TypedProvider 中的抽象函数)以正确地将该值向上传递到小部件树。

mixin TypedWidget<T,N extends TypedProvider<T>?> on Widget {
  ChangeNotifierProvider<N> asTypedWidget(
      BuildContext context,N Function(BuildContext) createProviderFunc) {
    return ChangeNotifierProvider<N>(
        create: createProviderFunc,builder: (BuildContext innerContext,_) {
          return WillPopScope(
              child: this,onWillPop: () {
                final N provider = Provider.of<N>(innerContext,listen: false);
                if (provider == null) {
                  return Future<bool>.value(true);
                }
                provider.onPopFunction(provider.getPopFunctionArgs());
                return Future<bool>.value(true);
              });
        });
  }

  /// Builds this TypedWidget inside `showDialog`.
  ///
  /// The `TypedProvider`'s `onPopFunction` is called if the
  /// dialog is closed outside of a manual `Navigator.pop()`. This doesn't have
  /// a return type; all returning actions should be done inside the defined
  /// `onPopFunction` of the provider.
  ///
  /// Example:
  /// ```
  /// MyTypedWidget().showTypedDialog(
  ///   context,///   (BuildContext context) => MyTypedProvider(...)
  /// );
  /// ```
  Future<void> showTypedDialog(
      BuildContext context,N Function(BuildContext) createProviderFunc,{bool barrierDismissible = true}) async {
    await showDialog<void>(
      context: context,barrierDismissible: barrierDismissible,builder: (_) => asTypedWidget(context,createProviderFunc),);
  }
}

abstract class TypedProvider<PopType> with ChangeNotifier {
  TypedProvider(this.onPopFunction);

  Function(PopType) onPopFunction;
  PopType getPopFunctionArgs();

  void pop(BuildContext context) {
    onPopFunction(getPopFunctionArgs());
    Navigator.pop(context);
  }
}

很可能还有其他方法可以实现这一点,这个解决方案当然有一些限制,但它有效地为对话框提供了强类型。