前提
Flutterアプリの画面で入力した内容を、
PHPで作成したAPIにリモートで飛ばすのですが
値を飛ばすことができません。
実現したいこと
Flutterの画面から入力した値をPHPで作成したAPIに送信する、ログイン機能を作成しようとしてます。
発生している問題・エラーメッセージ
Flutter側の送信でエラーが起こっているようです。
dart
1XMLHttpRequest error. 2No ScaffoldMessenger widget found. 3Login widgets require a ScaffoldMessenger widget ancestor. 4The specific widget that could not find a ScaffoldMessenger ancestor was: 5 Login 6The ancestors of this widget were: 7 [root] 8Typically, the ScaffoldMessenger widget is introduced by the MaterialApp at the top of your application widget tree.
該当のソースコード
dart
1//ログイン処理 2 Future<void> _login() async { 3 if (!_formKey.currentState!.validate()) { 4 return; 5 } 6 7 setState(() { 8 _isLoading = true; 9 }); 10 //dataに値が入っていることは確認できています 11 Map<String, String> data = {'email': _mail!, 'password': _password!}; 12 13 Response? res; 14 try { 15 //このPOST処理に入ったときにエラーが発生します。 16 res = await Network().postData(data, '/ReadUserInfo'); 17 } catch (e) { 18 // debugPrint(e.toString()); 19 print("エラー内容:"+e.toString()); 20 } 21 22 if (res == null) { 23 if (mounted) { 24 ScaffoldMessenger.of(context).showSnackBar( 25 const SnackBar(content: Text("エラーが発生しました。")), 26 ); 27 } 28 setState(() { 29 _isLoading = false; 30 }); 31 return; 32 } 33 34 var body = json.decode(res.body); 35 36 // エラーの場合 37 if (res.statusCode != 200) { 38 if (mounted) { 39 ScaffoldMessenger.of(context).showSnackBar( 40 SnackBar(content: Text(body['message'])), 41 ); 42 } 43 setState(() { 44 _isLoading = false; 45 }); 46 return; 47 } 48 49 // 正常終了の場合 50 SharedPreferences localStorage = await SharedPreferences.getInstance(); 51 localStorage.setString('token', json.encode(body['token'])); 52 localStorage.setString('user', json.encode(body['user'])); 53 54 if (!mounted) return; 55 Navigator.push( 56 context, 57 MaterialPageRoute(builder: (context) => const TabPage()), 58 ); 59 } 60 61//以下ログイン画面生成処理 62 63 Widget build(BuildContext context) { 64 return MaterialApp( 65 home:Scaffold( 66 appBar: AppBar( 67 centerTitle: true, 68 title: const Text("ログイン"), 69 ), 70 body: SafeArea( 71 child: _isLoading 72 ? const Center( 73 child: CircularProgressIndicator(), 74 ) 75 : Form( 76 key: _formKey, 77 child: Container( 78 padding: const EdgeInsets.all(16), 79 child: Column( 80 children: [ 81 TextFormField( 82 keyboardType: TextInputType.emailAddress, 83 decoration: const InputDecoration( 84 hintText: "メールアドレス", 85 ), 86 validator: (emailValue) { 87 if (emailValue == null || emailValue == "") { 88 return 'メールアドレスは必ず入力してください。'; 89 } 90 _mail = emailValue; 91 return null; 92 }, 93 ), 94 TextFormField( 95 keyboardType: TextInputType.visiblePassword, 96 obscureText: isHiddenPassword, 97 decoration: InputDecoration( 98 hintText: "パスワード", 99 suffixIcon: IconButton( 100 icon: Icon(isHiddenPassword 101 ? Icons.remove_red_eye 102 : Icons.visibility_off), 103 onPressed: () { 104 setState(() { 105 isHiddenPassword =!isHiddenPassword; 106 }); 107 }), 108 ), 109 110 validator: (passwordValue) { 111 if (passwordValue == null || 112 passwordValue == "") { 113 return 'パスワードは必ず入力してください。'; 114 } 115 _password = passwordValue; 116 return null; 117 }, 118 ), 119 const SizedBox( 120 height: 32, 121 ), 122 ElevatedButton( 123 onPressed: () { 124 _login(); 125 }, 126 child: const Text("ログイン")), 127 const SizedBox( 128 height: 16, 129 ), 130 ElevatedButton( 131 onPressed: () { 132 runApp(const SignUpPage() 133 ); 134 }, 135 child: const Text("会員登録")), 136 const SizedBox( 137 height: 16, 138 ), 139 ], 140 ), 141 ))))); 142 } 143
dart
1//APIへ接続するクラスです 2import 'dart:convert'; 3import 'package:shared_preferences/shared_preferences.dart'; 4import 'package:http/http.dart'; 5class Network { 6 // Androidシュミレーターを使う場合はlocalhostを10.0.2.2に変更する 7 //ここでPHP側のURLを指定(こちらのURLが正しいことは確認済みです。直接叩いて特定のファイルまで飛ぶことができます) 8 final String _url = 'https://XXX.XXX.XXX.XX/index.php'; 9 String? token; 10 11 // SharedPreferences(データを保存する仕組み)からトークンを取得 12 _setToken() async { 13 SharedPreferences localStorage = await SharedPreferences.getInstance(); 14 String? localToken = localStorage.getString('token'); 15 16 // なぜかlocalStorageから取得した値の前後に"が入るので仕方なくここで置換する 17 if (localToken != null) { 18 token = localToken.replaceAll('"', ''); 19 } 20 } 21 22 // ヘッダー情報をセット 23 _getHeaders() => { 24 'Content-type': 'application/json', 25 'Accept': 'application/json', 26 'Authorization': 'Bearer $token' 27 }; 28 29 // POST 30 //ここのapiUrlで、PHP側のログインAPIを指定(/login など) 31 Future<Response> postData(data, String apiUrl) async { 32 await _setToken(); 33 Uri fullUrl = Uri.parse(_url + apiUrl); 34//この時点でfullUrlとdataの中身が正しいことは確認済みです 35 return await post(fullUrl, body: jsonEncode(data), headers: _getHeaders()); 36 } 37 38 // GET 39 Future<Response> getData(String apiUrl) async { 40 await _setToken(); 41 Uri fullUrl = Uri.parse(_url + apiUrl); 42 Response res = await get(fullUrl, headers: _getHeaders()); 43 return res; 44 } 45} 46
試したこと
・作成するにあたって参考にしているサイトはこちらです。
https://qiita.com/atm_33/items/9ffc89caf1c2b6d6d661
・デバッグで確認したところ入力した内容は変数に格納されています。
(_mailと_passwordです)
・postDataメソッドを実行した後にエラーが発生します
・「XMLHttpRequest error.No ScaffoldMessenger widget found.」
をキーワードで検索した結果、同様の現象について書かれた記事があったので書かれている通りグローバルキーを作ってみましたが、結果は変わりませんでした。
https://stackoverflow.com/questions/66833689/flutter-no-scaffoldmessenger-widget-found
・デバッグで確認すると、API接続ファイルの方で作成しているlocal tokenがnullであることがわかりました。
これが原因かと思い、local tokenの作成方法を調べていますが、
すでに実装コードは書いているので、なぜnullなのかもわかっていない状態です。
追記です。
tokenがnullであることは、もしかしたら問題ないかもしれません。
定義の段階でtokenはnull許容であることと、
ここでのtokenはAPIから受け取る際に使われると参考サイトに書かれていたことから判断しました。
補足情報(FW/ツールのバージョンなど)
開発ツール:AndroidStudio4.01
DartSDKバージョン:2.17.6
Flutterバージョン:3.05

回答1件
あなたの回答
tips
プレビュー