🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

Dart

Dartは、Googleによって開発されたJavaScriptの代替となることを目的に作られた、ウェブ向けのプログラミング言語である。

Q&A

解決済

2回答

3124閲覧

[Flutter] 名前付きルートで引数を渡す画面遷移について

hgka3782

総合スコア1

Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

Dart

Dartは、Googleによって開発されたJavaScriptの代替となることを目的に作られた、ウェブ向けのプログラミング言語である。

0グッド

0クリップ

投稿2021/03/11 13:55

編集2021/03/17 09:23

API情報を受け取ってリスト化し、詳細を(changeTimeFormatしつつ)遷移先で表示したいのですが、画面遷移がうまくいきません。titleは表示させられているのでAPIコールはできていると思うのですが、やはり引数を遷移先に渡せていないのでしょうか?ご教授のほどよろしくお願いいたします。

環境
Flutter 1.22.4
Dart 2.10.4
イメージ説明

関連コード抜粋

dart

1 //main.dart 2 3void main() { 4 runApp(MyApp()); 5} 6 7class MyApp extends StatelessWidget { 8 // This widget is the root of your application. 9 10 Widget build(BuildContext context) { 11 return MaterialApp( 12 title: 'Connpass Event State Search App', 13 theme: ThemeData( 14 primarySwatch: Colors.blue, 15 visualDensity: VisualDensity.adaptivePlatformDensity, 16 ), 17 home: StateNotifierProvider<MainViewModel, MainViewModelData>( 18 create: (_) => MainViewModel(), 19 child: const MyHomePage(title: 'Connpass Event Search App'), 20 ), 21 routes: { 22 '/detail': (_) => Detail(), ← ここでエラー 23 }, 24 ); 25 } 26}

dart

1 //body.dart 2 3 final List<EventResponse> eventList = response != null ? response.events : []; 4 var body = eventList.isNotEmpty 5 ? ListView( 6 scrollDirection: Axis.vertical, 7 controller: _scrollController, 8 shrinkWrap: true, 9 children: eventList 10 .map((event) => 11 Card( 12 child: ListTile( 13 title: Text(event.title), 14 onTap: () { 15 Navigator.pushNamed( 16 context, 17 '/detail', 18 arguments: eventList, 19 ); 20 }, 21 ) 22 ) 23 ) 24 .toList())

dart

1 //detail.dart 2 3class Detail extends StatelessWidget { 4 EventResponse event; 5 Detail({Key key, this.event}) : super(key: key); 6 7 8 9 Widget build(BuildContext context) { 10 final List<EventResponse> args = ModalRoute.of(context).settings.arguments; 11121314Widget buildDetail() { 15 Map<String, String> detailMap = { 16 '開催日時': changeTimeFormat(event.startedAt), 17 '終了日時': changeTimeFormat(event.endedAt), 18 '会場': event.place, 19 '会場の所在地': event.address, 20 };

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

遷移先のDetailウィジェットのbuild()メソッド内で、
ModalRoute.ofを使用してargs変数で受け取っているので、
まず
print(args);
で確認して受け取れていれば、受け取り自体は成功している、ということになると思います。
あとはdetailMap変数で受け取る時にargsを使えば良いような気がします。

あとエラーに関しては、Detailコンストラクタのeventフィールドはnamed parameterなので渡さなくても良いと思いますが、渡さなければ自動的にeventフィールドはnullで初期化されると思うので、(null-safety導入前の話)
event.startedAt
などがnullへの参照となりエラー、ということですかね。
null-safety導入後なら、EventResponse型がnon-nullable型なので、デフォルト値を設定するなどしないとエラーが出るような気がします。

間違っていたらすみません笑

投稿2021/03/13 04:16

moriman

総合スコア615

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hgka3782

2021/03/13 17:19 編集

ご回答ありがとうございますm(_ _)m printで確認したら、args変数はうまく受け取れておりました!detailMap → argsDetailに置き換えたらうまく行けそうでした。 routesのDetailに関してはまだ理解に及ばず、躓いております。状況としましては、 カーソルを合わせると????add required argument 'event'`という警告が出て、 'Detail(event: null,),'に修正されますが、警告を消す為だけのコードになってしまいます。 null-safetyは導入前(理解していない為、触れていません)になると思われます。 print出力結果は以下のようになりました。 ``` flutter: [EventResponse(eventId: null, title: 【iCARE Dev Meetup #19】Front-End 最新アーキテクチャ, catchMessage: null, description: <h1>イベント概要</h1> <p>iCARE Developer Meetupは、月次で開催している株式会社iCAREが主催するエンジニア向けのLT勉強会です。</p> <p>実際のプロダクト開発で得た知見や技術を学ぶことができます。また外部の方を招いてLTを行なって頂くこともあります。</p> <p>注意:今回はオンラインでの配信となります。</p> <h2>YouTube Live:</h2> <p><a href="https://www.youtube.com/watch?v=wEiCn6uspr4" rel="nofollow">https://www.youtube.com/watch?v=wEiCn6uspr4</a></p> <h2>ミートアップやLTのアーカイブもアップロードしていますので、チャンネル登録お願いします!</h2> <p><a href="https://www.youtube.com/channel/UCYNfhylHYl6ggbB9uKaTxBg" rel="nofollow">https://www.youtube.com/channel/UCYNfhylHYl6ggbB9uKaTxBg</a></p> <h2>19回目の今<…> ```
moriman

2021/03/14 02:15

エラーが出ているとすると、 event.startedAt などの箇所でeventの値がnullだからエラーが出ているのだと推測されます。 なのでevent.startedAtなどのeventを参照するコードを消せばエラーは出なくなると思いますがどうでしょう。 ModalRoute.ofメソッドでEventResponseデータを取得しているので、この方法ならDetailクラスでeventフィールドを用意しなくてもできると思います。 https://flutter.dev/docs/cookbook/navigation/navigate-with-arguments ↑のページの二つ目の方法、MaterialAppのonGenerateRouteプロパティを使う方法だと、Detailクラスのeventフィールドに個々のEventResponseデータをセットすることもできるみたいです。
hgka3782

2021/03/17 09:23 編集

たびたび申し訳ございません、eventフィールドをコメントアウトするとflutter run ができなくなります。 ModalRoute.ofが適切に使用できてないのでしょうか?または、取得した文字列を変換して、widgetでbuildするあたりに原因があるのでしょうか? ----------エラー------------------------ Running Gradle task 'assembleDebug'... lib/view/detail.dart:42:32: Error: The getter 'event' isn't defined for the class 'Detail'. - 'Detail' is from 'package:flutter_connpass_api_app/view/detail.dart' ('lib/view/detail.dart'). Try correcting the name to the name of an existing getter, or defining a getter or field named 'event'. '開催日時': changeTimeFormat(event.startedAt), ^^^^^ =========省略===========
guest

0

ベストアンサー

statedAt,endedAt,eventUrlなど一部のフィールドが取得できていなくてnullが渡っているので、遷移時にエラーが出るようです。
その箇所を適当に書き換えて動くようにしたら、とりあえず動くので、
ページ遷移時のデータの渡し方自体はこれで良いと思います。

上記フィールドが取得できていないのはfreezedの使い方に原因があるような気がします。

//body.dart import 'package:provider/provider.dart'; import 'package:flutter/material.dart'; import 'package:flutter_connpass_api_app/model/connpass_response.dart'; import 'package:flutter_connpass_api_app/model/event_response.dart'; import 'package:flutter_connpass_api_app/view/main_view_model.dart'; import 'package:flutter_connpass_api_app/view/main_view_model_data.dart'; class MyHomePage extends StatefulWidget { const MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { // AppBarに検索バーを作成 final TextEditingController _search = TextEditingController(); ScrollController _scrollController; @override void initState() { super.initState(); _scrollController = ScrollController(); // Linstenerを追加して更新処理を実装する _scrollController.addListener(() { final maxScrollExtent = _scrollController.position.maxScrollExtent; final currentPosition = _scrollController.position.pixels; if (maxScrollExtent > 0 && (maxScrollExtent - 20.0) <= currentPosition) { // ↑ 下端位置から20pixelの位置に達したら、コンテンツを読み込む // position.maxScrollExtent => ListView全体の下端位置 // position.pixels => 現在の表示位置 } }); } /// ListView部分 @override Widget build(BuildContext context) { // StateNotifierのStateを読む // context.select<Data,T>でデータを読み出す // selectはデータに変化があった際に自動でrebuildしてくれる(copyWith) final response = context .select<MainViewModelData, ConnpassResponse>((data) => data.response); final state = context.select<MainViewModelData, MainViewModelState>( (data) => data.viewModelState); final List<EventResponse> eventList = response != null ? response.events : []; print("????$eventList"); // ListViewでJSONデータを表示 Widget body = eventList.isNotEmpty //Widget body = eventList.length > 0 //var body = eventList.isNotEmpty ? ListView( scrollDirection: Axis.vertical, controller: _scrollController, shrinkWrap: true, children: eventList.map((event) { print("${event.startedAt}"); return Card( child: ListTile( title: Text(event.title), onTap: () { Navigator.pushNamed( context, '/detail', //arguments: eventList, //遷移先はDetailページなので、eventListを渡すのではなく、 // eventを渡す方が自然な気がする。 arguments: event, ); }, ), ); }).toList()) // bodyの初期画面 : const Center( child: Padding( padding: EdgeInsets.all(24), child: Text( 'ここに検索結果を表示する', style: TextStyle(fontSize: 19), textAlign: TextAlign.center, ), ), ); if (state == MainViewModelState.loading) { body = const Center( child: CircularProgressIndicator(), ); } else if (state == MainViewModelState.error) { body = const Center( child: Padding( padding: EdgeInsets.all(24), child: Text( 'エラーが発生しました。検索ワードを変えてお試しください', style: TextStyle(fontSize: 19), textAlign: TextAlign.center, ), ), ); } /// AppBar 検索バー return Scaffold( appBar: AppBar( title: TextField( controller: _search, style: const TextStyle( color: Colors.white, ), decoration: const InputDecoration( labelText: '検索バー', labelStyle: TextStyle( color: Colors.white, ), hintStyle: TextStyle( color: Colors.white, ), )), actions: <Widget>[ IconButton( icon: const Icon(Icons.search), onPressed: () { context.read<MainViewModel>().fetch(_search.text); }) ], ), body: body, ); } }
//detail.dart import 'package:flutter/material.dart'; import 'package:flutter_connpass_api_app/model/event_response.dart'; import 'package:flutter/gestures.dart'; import 'package:intl/intl.dart'; import 'package:intl/date_symbol_data_local.dart'; import 'package:url_launcher/url_launcher.dart'; /// イベント詳細のレイアウト class Detail extends StatelessWidget { //Detail(EventResponse event); //const Detail(Type eventResponse, this.event); //Detail({Key key, @required this.event}) : super(key: key); @override Widget build(BuildContext context) { // パラメーターを取り出す final EventResponse args = ModalRoute.of(context).settings.arguments; print("${args.place}"); //変数受け取り確認 print("${args.startedAt}"); print("${args.address}"); return Scaffold( appBar: AppBar( title: const Text('イベント詳細'), ), body: Container( margin: const EdgeInsets.fromLTRB(10, 20, 30, 40), padding: const EdgeInsets.fromLTRB(10, 20, 50, 80), child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ buildDetail(args), buildUrl(args), ], ), ), ); } Widget buildDetail(EventResponse args) { Map<String, String> argsDetail = { //↓'開催日時'、'終了日時'のコメント外すと遷移時にエラー。 //args.startedAtなどがnullなのでエラーが出ていると思われる //多分freezedの使い方が正しくないので一部のフィールドが取得できずに //nullになっているのではないかと思われる。 //'開催日時': changeTimeFormat(args.startedAt), //'終了日時': changeTimeFormat(args.endedAt), '会場': args.place, '会場の所在地': args.address, }; //↓eventは使う必要は無い。 /* Map<String, String> argsDetail = { '開催日時': changeTimeFormat(event.startedAt), '終了日時': changeTimeFormat(event.endedAt), '会場': event.place, '会場の所在地': event.address, }; */ return Container( child: buildDetailRow(argsDetail) ); } Widget buildDetailRow(Map<String, String> argsDetail) { final detailList = <Widget>[]; argsDetail.forEach((key, value) { detailList.add(Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( flex: 1, child: Padding( padding: const EdgeInsets.fromLTRB(10, 5, 5, 5), child: Text(key ?? ''), ), ), Expanded( flex: 3, child: Padding( padding: const EdgeInsets.fromLTRB(10, 5, 10, 5), //child: Text(key ?? ''), child: Text(value ?? ''), ), ), ], )); }); return Column( mainAxisAlignment: MainAxisAlignment.start, children: detailList, ); } Widget buildUrl(EventResponse args) { return Container( padding: const EdgeInsets.fromLTRB(10, 15, 0, 0), child: RichText( textAlign: TextAlign.center, text: TextSpan( children: [ TextSpan( text: 'connpassページはこちらから', style: const TextStyle(color: Colors.lightBlue), recognizer: TapGestureRecognizer() ..onTap = () async { await launch( //args.eventUrlも取得できていないのでnullになっていると思われる。 //args.eventUrl, 'https://rakus.connpass.com/event/204540/', forceWebView: true, // ios内かブラウザのどちらで開くかを指定 trunはios forceSafariVC: true, // Android内かブラウザのどちらで開くかを指定 trunはAndroid ); } ) ], ), ), ); } // ISO-8601形式を「○○/○○/○○/○○:○○」に変換 String changeTimeFormat(String before) { initializeDateFormatting('ja_JP'); final datetime = DateTime.parse(before); final formatter = DateFormat('yyyy/MM/dd HH:mm'); final formatted = formatter.format(datetime); return formatted; } }

投稿2021/03/17 03:34

moriman

総合スコア615

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hgka3782

2021/03/17 09:21

無事、データの受け渡しができました。 何度も懇切丁寧にありがとうございました!! freezedに関しては、導入した際にファイルの生成がうまくいかずにごちゃごちゃしてしまった経緯があるので、それが関連してそうです。探してみます!ありがとうございました!
moriman

2021/03/17 10:21

よかったです。 がんばってください!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問