前回、FlutterでAPIを叩いてデータを取得するという記事を掲載しました。今回は、FlutterでPOSTデータを送信してデータ登録を行うところをやってみたいと思います。
前回のソースからの続きなので、前回分も参照して行われると分かりやすいと思います。
=> 前回の記事はこちらから
1.main.dart の変更
まずは、データ登録のための画面遷移を行いたいと思いますので、前回のソース(main.dart)を改修します。
import 'dart:async'; //非同期処理用 import 'dart:convert'; //httpレスポンスをJSON形式に変換用 import 'package:flutter/material.dart'; //google提供のUIデザイン import 'package:http/http.dart' as http; // 変更① import 'post.dart'; void main() { runApp(MaterialApp( // 変更②router追加 initialRoute: '/', routes: { '/': (context) => HomePage(), '/postpage': (context) => PostPage(), }, )); } class HomePage extends StatefulWidget { @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { late Map data; late List userData; Future getData() async { final response = await http.get(Uri.parse("<GET API URI>")); final body = json.decode(response.body); final articles = body.map((dynamic item) => item).toList(); setState(() { userData = articles; }); } @override void initState() { super.initState(); getData(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("sample API"), backgroundColor: Colors.green, ), body: ListView.builder( itemCount: userData == null ? 0 : userData.length, itemBuilder: (BuildContext context, int index) { return Card( child: Row( children: <Widget>[ CircleAvatar( child: Text( // 変更③ "${userData[index]["event_name"]}", style: TextStyle(color: Colors.black), ), ), // 変更④ Text( "${userData[index]["event_name"]} ${userData[index]["date_and_time"]}", textAlign: TextAlign.center, ), // 変更⑤画面遷移用のボタンを追加 RaisedButton( onPressed: () { Navigator.pushNamed(context, '/postpage'); }, child: Text( '参加する', ), color: Colors.green, ), ], ), ); }, )); } }す
少し解説していきます。
// 変更① import 'post.dart';
この部分は、新たに作成するページ(CLASS)を読み込んでいます。
// 変更②router追加 initialRoute: '/', routes: { '/': (context) => HomePage(), '/postpage': (context) => PostPage(), }, ) );
この部分は見ればなんとなくわかるかと思いますが、routerの役割に変更しました。
child: Text( "${userData[index]["event_name"]}", style: TextStyle(color: Colors.black), ),
変更③と④は、出力データを変更しました(見栄えの問題なのであまり気にしなくてもいいかも)。
// 画面遷移用のボタンを追加 RaisedButton( onPressed: () { Navigator.pushNamed(context, '/postpage'); }, child: Text( '参加する', ), color: Colors.green, ),
変更⑤の部分はボタンクリックで画面遷移を行うように変更しました。
ここでちょっとだけ覚えておきたいのは、routerを変更①のように記述した場合には、Navigator.pushではなく、Navigator.pushNamedにする必要があるということです。
一旦このようにmain.dartを変更したら、変更①のあたりでエラーが生じていると思います。
それもそのはずで、まだpost.dartにclassを作成していないからです。
それではここかからが本題で、内容に入っていきたいと思います。
2.post.dartを作成
まず、post.dartというファイルを作成します。
お約束ですが、importするモジュールを呼び出します。
import 'dart:convert'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http;こ
これはmain.dartと一緒です。
先にソース全体をお見せしたいと思います。
import 'dart:convert'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; class PostPage extends StatelessWidget { // Formを使うために必ず必要 final _formKey = GlobalKey<FormState>(); // データを取得するために必要 // String email = ''; final EmailFieldController = TextEditingController(); // 画面を作っていきます。 Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Post Page'), ), body: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ TextFormField( decoration: const InputDecoration( border: OutlineInputBorder(), filled: true, icon: Icon(Icons.person), hintText: '名前を入力してください。', labelText: '名前', ), controller: EmailFieldController, // validator: (value) { // if (value is Null) { // return '必須です。'; // } // return null; // }, // 三項演算子だとこうなる validator: (value) => value is Null || value.isEmpty ? '必須です' : null, // onSaved: (value) => () { // // var email = value!; // print('$value'); // }, ), Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: RaisedButton( onPressed: () async { if (_formKey.currentState!.validate()) { // post APIを投げる final String url = "<YOUR POST API URI>"; Map<String, String> headers = { 'content-type': 'application/json' }; String body = json.encode({'email': EmailFieldController.text}); http.Response resp = await http.post(Uri.parse(url), headers: headers, body: body); if (resp.statusCode != 201) { int statusCode = resp.statusCode; var content = "Failed to post $statusCode"; return; } var content = resp.body; } }, child: Text('Submit'), ), ), ], ), )); } }
初学の頃に陥りやすいところも記述しています。
・フォームの入力値の取得
例えば、
入力値を取得するためにプロパティを設定する以下の部分
// データを取得するために必要 // String email = '';
こんな感じで記述をすることが多いのではないでしょうか。
これだと入力値を取得することができません。
入力値を取得するためには、
final EmailFieldController = TextEditingController();
と記述して、TextEditingControllerを使用するのです。そうすると、値を取得する時には、
EmailFieldController.text
として、変数名.textで取得することができるようになります。
※ EmailなのにTextEditingControllerで取得しています。ツッコミはあると思いますが、適宜の方法に置き換えてください。
・validation部分の書き方
// validator: (value) { // if (value is Null) { // return '必須です。'; // } // return null; // },
公式サイトでもこのような記述がされています。
ですが、このくらいであれば三項演算子で記述した方がソースが短くて可読性も上がるかと思います。
・レスポンスの受け取り
if (resp.statusCode != 201) { int statusCode = resp.statusCode; var content = "Failed to post $statusCode"; return; } var content = resp.body;
レスポンスの受け取りでステータスをどのように返却するのかは仕様によって変わってくると思います。その場合は、ステータスコードを書き換える必要があると思います。
3.その他注意点
開発するときのバージョンには注意してください。
現在のdart、Flutterのバージョン(2021/6/27)ではNull Safetyにより、以下の書き方はエラーとなります。
validator: (value){ if (value.isEmpty){ return '必須です'; } return null; },
この場合、valueがnullの可能性があるのです。そのため、if文の中の条件式を以下のようにする必要があります。
validator: (value) { if (value is Null) { return '必須です。'; } return null; },
同じことがonPressの際にも生じます。
if (_formKey.currentState.validate()) { // post APIを投げる
これだと、_formKey.currentStateでエラーになると思います。
条件式を以下のように変更します。
if (_formKey.currentState!.validate()) { // post APIを投げる
単純に「!」を入れただけですが、これでNull Safetyになるということです。
swiftを経験されたことがあればなんとなくわかるかと思います。
こうすると、うまくいくかと思います。
後は、登録時の挙動(画面遷移など)を記述していけば問題なく出来上がりますね。
4.全体APP
こんな感じで画面が出ていればOKかなと思います。