Включение нулевой безопасности во 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);
}

на dartpad здесьhttps://dartpad.dev/?null_safety=true&amp;amp;id=7fc7f48276f696508799c960bf983400&amp;amp;run=true

Другие вопросы по тегам