Включение нулевой безопасности во Flutter ModalRoute нарушает аргументы, передаваемые в аргументы RouteSettings
Меня сводит с ума тот факт, что пример Flutter на для передачи аргументов именованному маршруту не работает с включенной нулевой безопасностью.
Вот пример, который я пытаюсь отладить. Есть
Consumer
создание представления списка с
ListView.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!;
так же, как это описано в официальной документации странице официальной документацииздесь . Но отладчик выдает мне ошибку:
Значение типа «Объект» не может быть присвоено переменной типа «ScreenArguments».
хотя я просто просматриваю пример и не могу найти правильный путь.
print(args.runtimeType);
показывает, что вдруг мои аргументы превращаются в неизвестные еще
_InternalLinkedHashMap<String, String>
и, следовательно, не может быть приведен как
ScreenArguments
в примере выше. Единственный способ получить аргументы, не устанавливая такой тип:
final args = ModalRoute.of(context)!.settings.arguments!;
но опять же невозможно ничего получить от этого объекта, поскольку он явно не реализует геттеры моего пользовательского объекта.
Я также пытался преобразовать переданный аргумент в виде строки из json-подобного объекта, но аргументы по-прежнему преобразуются в
_InternalLinkedHashMap
. Не понимаю, куда дальше идти...
ОБНОВИТЬ
Так что я не знаю, что именно произошло, но из того, что я вижу в производительности Android Studio, она сильно зависит от доступной мощности процессора, а также памяти. Я не стал чистить кеш, а перезапустил MacOS, соответственно все системные сборщики мусора должны были почистить какой-то системный кеш, потом перезапустил Android Studio и потом попытался закинуть
arguments: {
'title': menusData.menus[index].title.toString(),
}
как Карта, как это:
final args = ModalRoute.of(context)!.settings.arguments! as Map;
и вдруг печатает результат в дочернем виджете вот так
print(args);
print(args.runtimeType);
print(args['title']);
дает следующий результат:
I/flutter ( 8653): _InternalLinkedHashMap<String, String>
I/flutter ( 8653): my custom string
что не идеально, потому что мне все равно придется работать с классом ``, но я могу получить переданный аргумент String и использовать его в дочернем виджете. Имхо, это все еще не так, как должно работать...
1 ответ
Если ранее для параметра RouteSettings было установлено значение
arguments: {
'title': data.items[index].title,
}
и выполнял только горячие перезагрузки, состояние, сохраненное в дереве для вашего MaterialPageRouts->Settings->Arguments, останется этой картой. Выполняли ли вы горячий перезапуск или перезапускали приложение с тех пор, как начали замечать проблему? Это единственный способ увидеть, что неправильный тип объекта был передан.
Я вставил весь пример кода, измененный для работы с нулевой безопасностью ниже. Также см. это . Если это не работает для вас, попробуйте пересобрать приложение с нуля.
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);
}