Как передать нестроковые данные в именованный маршрут во Flutter?

Я использую Flutter для моего мобильного приложения. У меня много экранов, и я использую Навигатор. Я хотел бы использовать "именованные маршруты", но мне также нужно передать нестроковые (например, изображения) моему следующему маршруту.

Я не могу использовать pushNamed() потому что я не могу передать не строковые данные к нему.

Как я могу использовать именованный маршрут + отправить нестроковые данные?

13 ответов

Именованный маршрут в принципе должен быть самоописуемым. Вы не должны пытаться сделать что-то вроде pushNamed("/myRoute", myObject), Вместо этого вы должны сделать что-то вроде pushNamed("myRoute/${myObject.id}") а затем извлеките этот идентификатор из пути маршрута и используйте его для запроса необходимых данных.

Где onGenerateRoute доступны как MaterialApp свойство используется. Например, вы можете сделать что-то вроде этого:

  onGenerateRoute: (routeSettings) {
    var path = routeSettings.name.split('/');
    if (path[0] == "myRoute") {
      final foo = path.length > 1 ? int.parse(path[1]) : null;
      return new MaterialPageRoute(
        builder: (context) => new MyPage(foo: foo),
        settings: routeSettings,
      );
    }
    // fallback route here
  },

Вы можете использовать параметр routes вашего приложения для прямой передачи аргументов.

Нравится:

  routes: {
    HomePage.route: (_) => HomePage(),
    DetailsPage.route: (context) =>
        DetailsPage(ModalRoute.of(context).settings.arguments),
  },

В этом случае полный пример будет выглядеть следующим образом:

import 'package:flutter/material.dart';

    void main() => runApp(MyApp());

    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          initialRoute: HomePage.route,
          routes: {
            HomePage.route: (_) => HomePage(),
            DetailsPage.route: (context) =>
                DetailsPage(ModalRoute.of(context).settings.arguments),
          },
        );
      }
    }

    class HomePage extends StatelessWidget {
      static const String route = '/';

      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Container(),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              Navigator.pushNamed(context, '/details',
                  arguments: ScreenArguments(
                    'My Details',
                    'Some Message',
                  ));
            },
          ),
        );
      }
    }

    class DetailsPage extends StatelessWidget {
      static const String route = '/details';

      final ScreenArguments arguments;

      DetailsPage(this.arguments);

      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(arguments.title),
          ),
          body: Center(
            child: Text(arguments.message),
          ),
        );
      }
    }

    class ScreenArguments {
      final String title;
      final String message;

      ScreenArguments(this.title, this.message);
    }

Используя карты

Нажимая аргументы, вы можете отправить их в виде карты и делать то же самое при их извлечении.

например

При нажатии

Navigator.of(context).pushNamed(
              'second',
              arguments: {
                'title':'This is a String',
                       or
                 'Fx': This could be any widget or Function
              }

При извлечении аргументов на целевой странице

final routes=ModalRoute.of(context).settings.arguments as Map<String,String>;

    return Scaffold(
      appBar: AppBar(
              title: Text(routes['title']),
              ),
      body: Container(
        child: Center(
          child: RaisedButton(
            child: Text("Back"),
            onPressed: ()=>Navigator.of(context).pop(),
          ),
        ),
      ),
    );

and choose your map accordingly accordingly

С помощью onGenerateRoute при передаче маршрута легко передать сложные аргументы Navigator.pushNamed или же Navigator.pushReplacementNamed

Минимальная установка, чтобы показать концепцию будет

main.dart

import 'package:flutter/material.dart';
import 'package:navigator/routes.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Navigation Demo',
      theme: ThemeData(
        primarySwatch: Colors.teal,
      ),
      onGenerateRoute: (RouteSettings settings) {
        return MaterialPageRoute(
          builder: (BuildContext context) => makeRoute(
                context: context,
                routeName: settings.name,
                arguments: settings.arguments,
              ),
          maintainState: true,
          fullscreenDialog: false,
        );
      },
    );
  }
}

routes.dart

в _buildRoute Метод, мы проверяем имя маршрута и приводим аргументы к требуемому типу.

Недостатком является то, что тип должен быть определен заранее, если требуемый аргумент не является простым типом.

import 'package:flutter/material.dart';

import 'package:navigator/list.dart';
import 'package:navigator/details.dart';

Widget makeRoute(
    {@required BuildContext context,
    @required String routeName,
    Object arguments}) {
  final Widget child =
      _buildRoute(context: context, routeName: routeName, arguments: arguments);
  return child;
}

Widget _buildRoute({
  @required BuildContext context,
  @required String routeName,
  Object arguments,
}) {
  switch (routeName) {
    case '/':
      return ArticleList();
    case '/ArticleView':
      Article article = arguments as Article;
      return ArticleView(article: article);
    default:
      throw 'Route $routeName is not defined';
  }
}

Просмотры

list.dart

Создайте аргумент маршрута, используя определенный тип, Article в нашем случае.

import 'package:flutter/material.dart';
import 'package:navigator/details.dart' show Article;

class ArticleList extends StatefulWidget {
  @override
  _ArticleListState createState() => _ArticleListState();
}

class _ArticleListState extends State<ArticleList> {
  List<Article> articles = [
    Article(
        id: 1,
        title: 'Article 1',
        author_name: 'Nilotpal',
        summary: 'Article 1 summary'),
    Article(
        id: 2,
        title: 'Article 2',
        author_name: 'Mike',
        summary: 'Article 2 summary'),
  ];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Articles'),
      ),
      body: Center(
        child: Column(
          children: <Widget>[
            ListTile(
              title: Text('${articles[0].title}'),
              subtitle: Text('by ${articles[0].author_name}'),
              onTap: () {
                Navigator.of(context)
                    .pushNamed('/ArticleView', arguments: articles[0]);
              },
            ),
            ListTile(
              title: Text('${articles[1].title}'),
              subtitle: Text('by ${articles[1].author_name}'),
              onTap: () {
                Navigator.of(context)
                    .pushNamed('/ArticleView', arguments: articles[1]);
              },
            ),
          ],
        ),
      ),
    );
  }
}

details.dart

Определите тип для аргументов

import 'package:flutter/material.dart';

class Article {
  final int id;
  final String author_name;
  final String title;
  final String summary;

  Article(
      {@required this.id,
      @required this.author_name,
      @required this.title,
      @required this.summary});
}

class ArticleView extends StatelessWidget {
  final Article _article;

  ArticleView({@required Article article}) : _article = article;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('${_article.title}'),
      ),
      body: SafeArea(
        top: true,
        child: Center(
          child: Column(
            children: <Widget>[
              Text('${_article.author_name}'),
              Text('${_article.summary}'),
            ],
          ),
        ),
      ),
    );
  }
}

Здесь много гигантских решений, но вашу проблему можно решить буквально двумя строчками, используя эту библиотеку:https://pub.dev/packages/get

List users = [2,4,6,3]
Get.toNamed('/second', arguments: users);

на втором маршруте

print(Get.arguments);
// out: [2,4,6,3]

Преимущество его использования перед другими решениями заключается в том, что вы можете передавать через аргументы что угодно, даже экземпляры классов, если хотите, и вы можете получать аргументы из любого места, как в вашем пользовательском интерфейсе, так и в вашем блоке, контроллере, вашем провайдере и т. Д. без необходимости передавать аргументы в onGenerateRoute классу.

Поваренная книга Flutter показывает, как перейти на новую страницу и передать на нее нестроковые данные.

Передача данных на следующую страницу

Я начал с Navigator.pushedNamed() потому что это было просто, и у меня не было данных для передачи. Когда мои потребности изменились, и я хотел передать данные, я переключился на Navigator.push(),

Пример:

var nextPageData = {foo:'bar'};

Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => 
     MyNextPage(myData: nextPageData))
 );

Я хотел использовать навигатор именованных маршрутов со значениями, как показано ниже.

       Navigator.pushNamed(context, '/increaseBalanceAccountPage',
                        arguments: {'accountBalanceViewModel': result},);

поэтому я должен определить этот маршрут в виджете materialApp в начале приложения, но я должен указать параметры в экземпляре, чтобы решить свою проблему с некоторой модификацией кода @YuriyLuchaninov, как показано ниже:

      MaterialApp(
    initialRoute: "/",
    routes: {
      '/': (context) => SplashScreenPage(),
      "/increaseBalanceAccountPage":

(context) =>
      UserAccountBalancePage(accountBalanceViewModel: Map<String,Object>
          .from(ModalRoute.of(context)!.settings.arguments as Map).values.first as
      AccountBalanceViewModel)
},
.....

Я снимаю изображения с помощью камеры, а затем передаю их на страницу подтверждения, например:

   ImagePicker.pickImage(source: source).then((File file) {
    Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => MediaCaptured(file: file),
        ));
  });

Вы можете легко сделать то же самое с любым типом файлов или нестроковых данных.

var foo = "non-string data";
Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => MediaCaptured(foo: foo),
        ));

Назовите следующую страницу в маршруте по имени класса, как указано выше.

Просто убедитесь, что ваша новая страница принимает это в своем конструкторе.

 // Stateful Widget
class MediaCaptured extends StatefulWidget {
    MediaCaptured({ Key key, @required this.foo,}) : super(key: key);
    final var foo;
}

// StatelessWidget
class MediaCaptured extends StatelessWidget {
    MediaCaptured(this.foo);
    var foo;
}

Это довольно просто с этим pakage

ссылка: https://pub.dartlang.org/packages/navigate

Это обеспечивает ваши ожидания и прост в использовании

Navigate.navigate(context,
                      "home",
                      transactionType:TransactionType.fromLeft , // optional
                      replaceRoute: ReplaceRoute.thisOne, //optional
                      arg: {"transactionType":TransactionType.fromLeft,"replaceRoute":ReplaceRoute.thisOne} //optional
                      );

Из первого класса StateFul:

      Navigator.of(context).pushNamed('/pending_order',arguments: {"staff" : staffObj});

Ко второму классу StateFul:

        class PendingOrders extends StatefulWidget {
  @override
  _PendingOrdersState createState() => _PendingOrdersState();
}

class _PendingOrdersState extends State<PendingOrders> {
  StaffModel staffModelObj;

  @override
  Widget build(BuildContext context) {
    final routes =
        ModalRoute.of(context).settings.arguments as Map<String, dynamic>;
    if (routes != null) {
      staffModelObj = routes["staff"];
    }
    return Scaffold(...);}}

Рассмотрим этот тривиальный пример из флаттера. У вас есть класс, созданный следующим образом

      class ScreenArguments {
   final String title;
   final String message;

  ScreenArguments(this.title, this.message);
}

Теперь мы будем передавать объект этого класса в качестве аргумента следующим образом

      Navigator.pushNamed(
  context,
  ExtractArgumentsScreen.routeName,
  arguments: ScreenArguments(
    'Extract Arguments Screen',
    'This message is extracted in the build method.',
  ),
);

И тогда вы можете извлечь аргументы следующим образом

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

И это все. Надеюсь, это поможет Источник: передача аргументов именованному маршруту

We can pass any type of arguments when declaring routes as constructor arguments as below,

For example to send a list of Strings,

List<String> titles = [];

void main() => runApp(
      new MaterialApp(
        home: new FirstPage(),
        routes: <String, WidgetBuilder>{
          "/SecondPage": (BuildContext context) => new SecondPage(titles),
        },
      ),
    );

class FirstPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Container(
      child: new RaisedButton(onPressed: () {
        Navigator.of(context).pushNamed('/SecondPage');
      }),
    );
  }
}

class SecondPage extends StatelessWidget {
  final List<String> titles;

  SecondPage(this.titles);

  @override
  Widget build(BuildContext context) {
    return new ListView.builder(
      itemBuilder: (context, index) {
        return new ListTile(
          title: new Text(titles[index]),
        );
      },
    );
  }
}
Другие вопросы по тегам