null safety ON在Flutter ModalRoute中破坏传递给routessettings参数的参数.&



在官方文档页面上的Flutter示例中,将参数传递给命名路由时,启用了null-safety,但失败了,这让我很抓狂。

这是我试图调试的例子。有一个ConsumerListView.builder构建列表视图,它返回以下内容:

return ListTile(
title: Text(data.items[index].title),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailsScreen(),
settings: RouteSettings(
// arguments: data.items[index] // <- this does not work
arguments: {
'title': data.items[index].title,  // <- read below
}
),
),
);
},
);

在DetailsScreen我试图得到如下参数:

final ScreenArguments args = ModalRoute.of(context)!.settings.arguments!;

就像这里的官方文档中描述的那样。但是调试器给了我一个错误:

Object类型的值不能赋值给ScreenArguments类型的变量

虽然我只是通过例子,不能找到正确的方法。print(args.runtimeType);表明我的论点突然被转换为未知的_InternalLinkedHashMap<String, String>,因此不能在上面的例子中被转换为ScreenArguments。获取参数的唯一方法是不像这样设置类型

final args = ModalRoute.of(context)!.settings.arguments!;

,但它不可能从这个对象中获得任何东西,因为它没有实现我的自定义对象的getter。

我还试图将传递的参数转换为json类对象的字符串,但参数仍然转换为_InternalLinkedHashMap。我不知道该往哪里走…


更新所以我不知道到底发生了什么,但从我在Android Studio的性能中看到,它在很大程度上取决于可用的CPU能力以及内存。我没有清理缓存,但是重新启动了MacOS,因此所有的系统垃圾收集器应该已经清理了一些系统缓存,然后重新启动Android Studio,然后尝试转换

arguments: {
'title': menusData.menus[index].title.toString(),
}

as Map like this

final args = ModalRoute.of(context)!.settings.arguments! as Map;

,然后突然在子Widget中打印结果,如下所示

print(args);
print(args.runtimeType);
print(args['title']);

产生以下结果:

I/flutter ( 8653): _InternalLinkedHashMap<String, String>
I/flutter ( 8653): my custom string

并不完美,因为我仍然必须使用' '类,但我能够获得传递的String参数并在子Widget中使用它。在我看来,这仍然不是它应该如何工作的…

如果你之前将routessettings设置为

arguments: {
'title': data.items[index].title,
}

并且只进行热重载,保存到树中的状态为您的MaterialPageRouts->Settings->Arguments将保持该映射。在发现问题后,您是否进行了热重启或重新启动应用程序?这是我唯一能看到传递错误对象类型的方法。

我已经粘贴了整个代码示例,修改后可以在下面使用null-safety。也可以在dartpad上看到。如果它不适合你,尝试重新构建应用程序。

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
// Provide a function to handle named routes. Use this function to
// identify the named route being pushed, and create the correct
// Screen.
onGenerateRoute: (settings) {
// If you push the PassArguments route
if (settings.name == PassArgumentsScreen.routeName) {
// Cast the arguments to the correct type: ScreenArguments.
final ScreenArguments args = settings.arguments! as ScreenArguments;
// Then, extract the required data from the arguments and
// pass the data to the correct screen.
return MaterialPageRoute(
builder: (context) {
return PassArgumentsScreen(
title: args.title,
message: args.message,
);
},
);
}
// The code only supports PassArgumentsScreen.routeName right now.
// Other values need to be implemented if we add them. The assertion
// here will help remind us of that higher up in the call stack, since
// this assertion would otherwise fire somewhere in the framework.
assert(false, 'Need to implement ${settings.name}');
return null;
},
title: 'Navigation with Arguments',
home: HomeScreen(),
routes: {
ExtractArgumentsScreen.routeName: (context) =>
ExtractArgumentsScreen(),
});
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Screen'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// A button that navigates to a named route that. The named route
// extracts the arguments by itself.
ElevatedButton(
child: Text("Navigate to screen that extracts arguments"),
onPressed: () {
// When the user taps the button, navigate to a named route
// and provide the arguments as an optional parameter.
Navigator.pushNamed(
context,
ExtractArgumentsScreen.routeName,
arguments: ScreenArguments(
'Extract Arguments Screen',
'This message is extracted in the build method.',
),
);
},
),
// A button that navigates to a named route. For this route, extract
// the arguments in the onGenerateRoute function and pass them
// to the screen.
ElevatedButton(
child: Text("Navigate to a named that accepts arguments"),
onPressed: () {
// When the user taps the button, navigate to a named route
// and provide the arguments as an optional parameter.
Navigator.pushNamed(
context,
PassArgumentsScreen.routeName,
arguments: ScreenArguments(
'Accept Arguments Screen',
'This message is extracted in the onGenerateRoute function.',
),
);
},
),
],
),
),
);
}
}
// A Widget that extracts the necessary arguments from the ModalRoute.
class ExtractArgumentsScreen extends StatelessWidget {
static const routeName = '/extractArguments';
@override
Widget build(BuildContext context) {
// Extract the arguments from the current ModalRoute settings and cast
// them as ScreenArguments.
final ScreenArguments args = ModalRoute.of(context)!.settings.arguments as ScreenArguments;
return Scaffold(
appBar: AppBar(
title: Text(args.title),
),
body: Center(
child: Text(args.message),
),
);
}
}
// A Widget that accepts the necessary arguments via the constructor.
class PassArgumentsScreen extends StatelessWidget {
static const routeName = '/passArguments';
final String title;
final String message;
// This Widget accepts the arguments as constructor parameters. It does not
// extract the arguments from the ModalRoute.
//
// The arguments are extracted by the onGenerateRoute function provided to the
// MaterialApp widget.
const PassArgumentsScreen({
Key? key,
required this.title,
required this.message,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Text(message),
),
);
}
}
// You can pass any object to the arguments parameter. In this example,
// create a class that contains both a customizable title and message.
class ScreenArguments {
final String title;
final String message;
ScreenArguments(this.title, this.message);
}

https://dartpad.dev/?null_safety=true& id = 7 fc7f48276f696508799c960bf983400&运行= true

最新更新