Chapter 07

Flutterスマホアプリ・MultiPart以外(ファイル項目、画像項目がない)の例-4/4

kazpgm
kazpgm
2022.06.14に更新

■商品情報詳細画面 shohin_detail.dart

flutter_app\lib\affairs\shohin\shohin_detail.dart

shohin_detail.dart
/// 商品情報表示画面
class ShohinDetail extends StatefulWidget {
  final String title;

username:ログインユーザー名
cookies:HTTPで取得したcookies
headers:HTTPで取得したheaders
resData:HTTPで取得したresData

  final String username;
  final Map<String, String> cookies;
  final Map<String, String> headers;
  final String resData;

  const ShohinDetail({Key? key, required this.title, required this.username,
    required this.headers, required this.cookies, required this.resData}) : super(key: key);
  
  State<ShohinDetail> createState() => _ShohinDetailState();
}

class _ShohinDetailState extends State<ShohinDetail> {
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  String _errorSuccessMsg = "";
  late ShohinForm _shohinForm;

_scrollController:画面を先頭に戻す(エラー・成功メッセージ行)ため使用する。

  late ScrollController _scrollController;

  
  Widget build(BuildContext context) {

日付は日本にしておく。

    initializeDateFormatting("ja_JP");
    return Scaffold(

      appBar: AppBar(
        title: const Text("商品情報詳細",
            overflow: TextOverflow.clip,
            style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
        centerTitle:true,
        backgroundColor: Colors.green,
        // ログアウト
        actions: <Widget>[
          IconButton(
            onPressed: () async {

ログアウトはCommUtils.onSignOutCommメソッドを使用する。

              String? _msg = await CommUtils.onSignOutComm(
                  context, _scrollController,
                  widget.headers, widget.cookies);
              if (_msg != "") {
                setState(() {
                  _errorSuccessMsg = _msg;
                });
              }
            },
            icon: const Icon(Icons.exit_to_app),
          ),
        ],
      ),
      // 画面に収まりきれないので、ListViewを使う
      body: Form(
        key: _formKey,
        child : ListView(

controller: _scrollControllerとし、画面を先頭に戻す(エラー・成功メッセージ行)ため使用する。

          controller: _scrollController,

bodyはListViewとし、children:_makeWidgetsメソッドとする。
_makeWidgetsメソッドは List<Widget> を戻している。

          children: _makeWidgets(),
        ),
      ),
    );
  }

  // 表示するWidgets
  List<Widget> _makeWidgets() {
    var contentWidgets = List<Widget>.empty(growable: true);
    //var contentWidgets = <Widget>[];
    if (_errorSuccessMsg.isNotEmpty) {
      contentWidgets.add(
        // エラーメッセージ
        Container(
          padding: const EdgeInsets.fromLTRB(20.0, 10, 20.0, 5.0),
          child:Text(_errorSuccessMsg,
            textAlign: TextAlign.center,
            overflow: TextOverflow.clip,
            style: const TextStyle(fontWeight: FontWeight.bold, color: Colors.red),),
        ),
      );
    } else {

エラーが無いとき

      contentWidgets.add(const SizedBox(
        height : 10.0,
      ));
    }

項目値が入っている場合

    if (CommUtils.chgToString(_shohinForm.resDetailMapForm["products"]) != "") {
      contentWidgets.add(

        Container(
          margin: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 5),
          padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0),
          child:Text('商品CD : ' + CommUtils.chgToString(_shohinForm.resDetailMapForm["products"]),
            textAlign: TextAlign.left,
            overflow: TextOverflow.clip,
          ),
        )
      );
    } else {
      contentWidgets.add(
        Container(
          margin: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 5),
          padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0),
          child:const Text('商品CD : ',
            textAlign: TextAlign.left,
            overflow: TextOverflow.clip,
          ),
        )
      );
    }

項目値が入っている場合

    if (CommUtils.chgToString(_shohinForm.resDetailMapForm["productsname"]) != "") {
      contentWidgets.add(

        Container(
          margin: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 5),
          padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0),
          child:Text('商品名 : ' + CommUtils.chgToString(_shohinForm.resDetailMapForm["productsname"]),
            textAlign: TextAlign.left,
            overflow: TextOverflow.clip,
          ),
        )
      );
    } else {
      contentWidgets.add(
        Container(
          margin: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 5),
          padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0),
          child:const Text('商品名 : ',
            textAlign: TextAlign.left,
            overflow: TextOverflow.clip,
          ),
        )
      );
    }

項目値が入っている場合

    if (_shohinForm.resDetailMapForm["biztypeCdObj"] != null) {
      contentWidgets.add(

        // 業種ID
        Container(
          margin: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 5),
          padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0),
          child:Text('業種ID : ' + _shohinForm.resDetailMapForm["biztypeCdObj"]["bizname"],
            textAlign: TextAlign.left,
            overflow: TextOverflow.clip,
          ),
        )
      );
    } else {
      contentWidgets.add(
        Container(
          margin: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 5),
          padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0),
          child:const Text('業種ID : ',
            textAlign: TextAlign.left,
            overflow: TextOverflow.clip,
          ),
        )
      );
    }

項目値が入っている場合

    if (_shohinForm.resDetailMapForm["categoryCdObj"] != null) {
      contentWidgets.add(

        // 種類・大分類
        Container(
          margin: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 5),
          padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0),
          child:Text('種類・大分類 : ' + _shohinForm.resDetailMapForm["categoryCdObj"]["catname"],
            textAlign: TextAlign.left,
            overflow: TextOverflow.clip,
          ),
        )
      );
    } else {
      contentWidgets.add(
        Container(
          margin: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 5),
          padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0),
          child:const Text('種類・大分類 : ',
            textAlign: TextAlign.left,
            overflow: TextOverflow.clip,
          ),
        )
      );
    }

項目値が入っている場合

    if (_shohinForm.resDetailMapForm["subcategoryCdObj"] != null) {
      contentWidgets.add(

        // 種類・中分類
        Container(
          margin: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 5),
          padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0),
          child:Text('種類・中分類 : ' + _shohinForm.resDetailMapForm["subcategoryCdObj"]["subcatname"],
            textAlign: TextAlign.left,
            overflow: TextOverflow.clip,
          ),
        )
      );
    } else {
      contentWidgets.add(
        Container(
          margin: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 5),
          padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0),
          child:const Text('種類・中分類 : ',
            textAlign: TextAlign.left,
            overflow: TextOverflow.clip,
          ),
        )
      );
    }

項目値が入っている場合

    if (_shohinForm.resDetailMapForm["extracategoryCdObj"] != null) {
      contentWidgets.add(

        // 種類・小分類
        Container(
          margin: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 5),
          padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0),
          child:Text('種類・小分類 : ' + _shohinForm.resDetailMapForm["extracategoryCdObj"]["excatname"],
            textAlign: TextAlign.left,
            overflow: TextOverflow.clip,
          ),
        )
      );
    } else {
      contentWidgets.add(
        Container(
          margin: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 5),
          padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0),
          child:const Text('種類・小分類 : ',
            textAlign: TextAlign.left,
            overflow: TextOverflow.clip,
          ),
        )
      );
    }

項目値が入っている場合

    if (_shohinForm.resDetailMapForm["openkbn1Name"].isNotEmpty) {
      contentWidgets.add(

        Container(
          margin: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 5),
          padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0),
          child:Text('公開区分 : ' + _shohinForm.resDetailMapForm["openkbn1Name"],
            textAlign: TextAlign.left,
            overflow: TextOverflow.clip,
          ),
        )
      );
    } else {
      contentWidgets.add(
        Container(
          margin: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 5),
          padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0),
          child:const Text('公開区分 : ',
            textAlign: TextAlign.left,
            overflow: TextOverflow.clip,
          ),
        )
      );
    }

項目値が入っている場合

    if (_shohinForm.resDetailMapForm["insdt"].isNotEmpty) {
      contentWidgets.add(

        Container(
          margin: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 5),
          padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0),
          child:Text('登録日時 : ' + DateFormat('yyyy/MM/dd HH:mm:ss').format(
                  (DateTime.parse(_shohinForm.resDetailMapForm["insdt"])).add(const Duration(hours:9))),
            textAlign: TextAlign.left,
            overflow: TextOverflow.clip,
          ),
        )
      );
    } else {
      contentWidgets.add(
        Container(
          margin: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 5),
          padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0),
          child:const Text('登録日時 : ',
            textAlign: TextAlign.left,
            overflow: TextOverflow.clip,
          ),
        )
      );
    }

項目値が入っている場合

    if (_shohinForm.resDetailMapForm["updt"].isNotEmpty) {
      contentWidgets.add(

        Container(
          margin: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 5),
          padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0),
          child:Text('更新日時 : ' + DateFormat('yyyy/MM/dd HH:mm:ss').format(
                  (DateTime.parse(_shohinForm.resDetailMapForm["updt"])).add(const Duration(hours:9))),
            textAlign: TextAlign.left,
            overflow: TextOverflow.clip,
          ),
        )
      );
    } else {
      contentWidgets.add(
        Container(
          margin: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 5),
          padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0),
          child:const Text('更新日時 : ',
            textAlign: TextAlign.left,
            overflow: TextOverflow.clip,
          ),
        )
      );
    }

    contentWidgets.add(
      Row (
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[

           Container(
             margin: const EdgeInsets.fromLTRB(5.0, 0, 5.0, 5),
             padding: const EdgeInsets.fromLTRB(5.0, 0, 5.0, 0),
               child: ElevatedButton(
                 onPressed: (){httpForUpd();},
                 style: ElevatedButton.styleFrom(
                   primary: Colors.blue,
                 ),
                 child: const Text("変更"),
               ),
           ),
          Container(
            margin: const EdgeInsets.fromLTRB(5.0, 0, 5.0,5),
            padding: const EdgeInsets.fromLTRB(5.0, 0, 5.0, 0),
            child: ElevatedButton(
              onPressed: (){httpForListBack();},
              style: ElevatedButton.styleFrom(
                primary: Colors.blue,
              ),
              child: const Text("一覧に戻る"),
            ),
          ),
        ]
      ),
    );
    return contentWidgets;
  }

  /// 情報一覧リストへ、updで、httpアクセス
  void httpForUpd() {
    httpForInfo("upd", "${Consts.myHttpUrl}/members/admin/shohin/shohin/upd");
  }
  /// 情報一覧リストへ、list_backで、httpアクセス
  void httpForListBack() {
    httpForInfo("list_back", "${Consts.myHttpUrl}/members/admin/shohin/shohin/list_back");
  }

情報変更、情報一覧へのhttpアクセス

  /// 情報変更、情報一覧へのhttpアクセス
  void httpForInfo(String _mode, String _url) async {
    try {
      http.Response response;
      if (_shohinForm.messageForm.mode =="list_back") {
        SrchOrderFormInMsgForm srchOrderFromInMsgForm = SrchOrderFormInMsgForm.initData();
        String _shohinSrchFormJson = srchOrderFromInMsgForm.toJson(_mode, "${widget.cookies['XSRF-TOKEN']}", _shohinForm.messageForm.page);
        widget.headers["content-type"]= "application/json; charset=UTF-8";
        widget.headers["X-XSRF-TOKEN"]= "${widget.cookies['XSRF-TOKEN']}";
        final url = Uri.parse(_url);

headersでcsrfとJsessionを渡している。

        response = await http.post(url,headers: widget.headers, body: _shohinSrchFormJson);
      } else {
        _shohinForm.products = CommUtils.chgToString(_shohinForm.resDetailMapForm["products"]);
        String _shohinFormJson = _shohinForm.toJson(_mode, "${widget.cookies['XSRF-TOKEN']}", _shohinForm.messageForm.page);
        widget.headers["content-type"]= "application/json; charset=UTF-8";
        widget.headers["X-XSRF-TOKEN"]= "${widget.cookies['XSRF-TOKEN']}";
        final url = Uri.parse(_url);

HTTPで、情報を取得している。

        response = await http.post(url,headers: widget.headers, body: _shohinFormJson);
      }
      if (response.statusCode != 200) {
        setState(() {
          int statusCode = response.statusCode;
          if (response.statusCode == 401 || response.statusCode == 403) {

response.statusCodeが200(正常)以外のとき
・ログインが必要な時は "ログインしてください"
・それ以外のステータスの時は”エラーが発生しました ”を表示している。

            _errorSuccessMsg = "ログインしてください";
          } else {
            _errorSuccessMsg = "エラーが発生しました $statusCode";
          }
        });
        // 画面を先頭に戻す
        _scrollController.animateTo(0,
            duration: const Duration(milliseconds:600),
            curve: Curves.easeInQuint);
        return;
      }

HTTPヘッダー内容を保存している

      CommUtils.updateCookie(response, widget.cookies, widget.headers);

HTTPのresponse.bodyをString形式にしている。

      // response.bodyをutf8でdecodeする。
      String _resData = utf8.decode(response.body.runes.toList());
      if (kDebugMode) {
        print(_resData);
      }
      // バックエンドで例外発生の場合MessageFormの値しか戻らないため、ここで確認する
      MessageForm _messageForm = MessageForm.initData();
      _messageForm.fromJson(_resData);

バックエンドで例外発生の場合MessageFormの値しか戻らないため、ここで確認する。

      // SpringBootで例外発生の場合
      if (_messageForm.mode =="SystemError") {
        // エラー画面
        Navigator.of(context).push(
          MaterialPageRoute(
            builder: (context) => Error(title: widget.title, username: widget.username,
                headers: widget.headers, cookies: widget.cookies, resData: _resData),
          ),
        );
        // 正常処理
      } else {
        if (_messageForm.mode == 'list_back') {

一覧画面を表示する時は、2画面戻ってから表示する。

          // 2画面戻る
          int count = 0;
          Navigator.popUntil(context, (_) => count++ >= 2);
          //情報一覧
          Navigator.of(context).push(
            MaterialPageRoute(
              builder: (context) =>
                  ShohinList(title: widget.title,
                      username: widget.username,
                      headers: widget.headers,
                      cookies: widget.cookies,
                      resData: _resData),
            ),
          );
        } else {

変更画面を表示する時は、1画面戻ってから表示する。

          // 1画面戻る
          int count = 0;
          Navigator.popUntil(context, (_) => count++ >= 1);
          //情報登録
          Navigator.of(context).push(
            MaterialPageRoute(
              builder: (context) =>
                  ShohinRegisterAmend(title: widget.title,
                      username: widget.username,
                      headers: widget.headers,
                      cookies: widget.cookies,
                      resData: _resData),
            ),
          );
        }
      }
    } catch (e) {
      setState(() {
        if (kDebugMode) {

例外が発生した時エラー表示。

          print("エラーが発生しました" + e.toString());
        }
        _errorSuccessMsg = "内部エラーが発生しました";
      });
    }
  }

    
  void initState() {
    super.initState();
    // 画面初期データを設定する
    _shohinForm = ShohinForm.initData();

HTTPで取得した内容を _shohinFormに設定している。

    _shohinForm.fromJson(widget.resData);
    _scrollController= ScrollController();
  }

    
  void dispose() {

画面を先頭に戻す(エラー・成功メッセージ行)ため使用している_scrollControllerをdisposeする

    _scrollController.dispose();
    // Clean up the focus node when the Form is disposed.
    super.dispose();
  }
}