BETA

Flutter で Hello World②

投稿日:2020-07-28
最終更新:2020-07-28

入金処理のためのダイアログを表示する

まず入金額を入力する画面を想像してみる。
別のページを作って遷移させてもいいが、入力内容はせいぜい金額くらいなので1画面使うほどの情報量じゃない。となると、ダイアログを使って入力するのが妥当だろうか。

import 'package:lunch_wallet/model/accounting.dart';  

      // onPressed: () {  
      // },  
      onPressed: () {  
        showDepositDialog(context: context);  
      },  

まずは、depositButton.dartのonPressedに機能を割り当てる。showDepositDialogはaccounting.dartに実装する予定のまだ存在しないメソッド。

import 'package:flutter/material.dart';  
import 'package:lunch_wallet/view/dialog.dart';  

// 入金ダイアログ表示  
Future showDepositDialog({  
  @required BuildContext context,  
  TransitionBuilder builder,  
  bool useRootNavigator = true,  
}) {  
  final Widget dialog = DepositDialog();  
  return showDialog(  
    context: context,  
    useRootNavigator: useRootNavigator,  
    builder: (BuildContext context) {  
      return builder == null ? dialog : builder(context, dialog);  
    },  
  );  
}  

次に、ビジネスロジックだけを集約するaccounting.dartを準備した。この中でダイアログを表示させるための実装を行う。

import 'package:flutter/material.dart';  

class DepositDialog extends StatefulWidget {  
  @override  
  State createState() => _DepositDialogState();  
}  

class _DepositDialogState extends State<DepositDialog> {  
  final dateTextController = TextEditingController();  

  @override  
  Widget build(BuildContext context) {  
    final List<Widget> actions = [  
      FlatButton(  
        child: Text(localizations.cancelButtonLabel),  
        onPressed: () => Navigator.pop(context),  
      ),  
      FlatButton(  
        child: Text(localizations.okButtonLabel),  
        onPressed: () {  
          int amount = int.tryParse(dateTextController.text);  
          Navigator.pop<int>(context, amount);  
        },  
      ),  
    ];  
    final AlertDialog dialog = AlertDialog(  
      title: Text('入金額'),  
      content: TextField(  
        controller: dateTextController,  
        decoration: InputDecoration(  
          hintText: '半角数字で入力してください',  
        ),  
        autofocus: true,  
        keyboardType: TextInputType.number,  
      ),  
    );  

    return dialog;  
  }  

  @override  
  void dispose() {  
    dateTextController.dispose();  
    super.dispose();  
  }  
}  

最後に表示させるダイアログのレイアウトを実装するdialog.dart。
ここまで実装して動かしてみた結果だ。

入金ボタンを押すと無事にダイアログが出現した。何よりびっくりしたのが、当初は面倒そうだと予想していたキーボードに関する実装がただの1行で済んでしまったことだ。実に簡単で素晴らしい。

BOTTOM OVERFLOWの正体

ダイアログが表示されるようになったのはよいが、キーボードの上側に黒と黄色の斜線が現れるようになった。よくよく画面を見てみると、小さな文字でBOTTOM OVERFLOW BY 181 PIXELSと表示されている。OKなりキャンセルのボタンを押すと斜線は消え、再度入金ボタンを押しても斜線は現れる。
これはキーボードの出現がかかわっているようだ。よくよく調べてみると、この原因はScaffoldにあったことが分かった。

For example, if there is an onscreen keyboard displayed above the scaffold, the body can be resized to avoid overlapping the keyboard, which prevents widgets inside the body from being obscured by the keyboard.

resizeToAvoidBottomInsetプロパティの初期値はtrueで、ボディのサイズが変更され得る状態にある。黒と黄色の斜線はそのリサイズの際にうまく処理できなかった部分の残骸となっているようだ。

class MyHomePage extends StatelessWidget {  
  @override  
  Widget build(BuildContext context) {  
    return Scaffold(  
      // キーボードが表示されてもbottom overflowが発生しないようにする  
      resizeToAvoidBottomInset: false,  
      // ヘッダ  
      appBar: Header(),  
      // 本体  
      body: Body(),  
      // フローティングボタン  
      // floatingActionButton: Button(),  
    );  
  }  
}  

cntents.dartにresizeToAvoidBottomInsetを初期値falseで追記する。フローティングボタンは現時点では実装していないのでコメントにしている。

これで目に付く斜線は消え去った。

数字だけ入力できるダイアログが欲しい

入金処理のダイアログは金額を入力するものなので、予めキーボードタイプは数字をメインにしたセット、TextInputType.numberを指定している。このタイプは数字と一部の記号が入力できるようになっているが、実際に金額を入力する際には、記号は使ってほしくない。
思いつく方法は、2つある。

  1. 入力値にバリデーションをかけ、数値以外のキャラクタが存在したらエラーメッセージなどを表示する方法
  2. そもそも数値以外の入力を拒否する方法

    import 'package:flutter/services.dart';  
    
     final AlertDialog dialog = AlertDialog(  
       title: Text('入金額'),  
       content: TextField(  
         controller: dateTextController,  
         decoration: InputDecoration(  
           hintText: '半角数字で入力してください',  
         ),  
         autofocus: true,  
         keyboardType: TextInputType.number,  
         inputFormatters: <TextInputFormatter> [  
           WhitelistingTextInputFormatter.digitsOnly,  
         ],  
       ),  
     );  

    テキストフィールドにinputFormattersプロパティを指定した。WhitelistingTextInputFormatter.digitsOnlyは数値入力のみ許可する指定になる。(厳密には数値のみがホワイトリストに登録されている状態で、数値以外のキーボードの入力も受け付けるが、ホワイトリストに登録されていないためインタラプトされて捨てられている)
    これで後者の、数値以外の入力を拒否することができた。

入力された値を呼び出し元に返す

数字のみの入力となったダイアログから、今度は値を返却する必要がある。実はこの機能はすでに実装済みなのでまずは簡単に説明する。

    final List<Widget> actions = [  
      FlatButton(  
        child: Text(localizations.cancelButtonLabel),  
        onPressed: () => Navigator.pop(context),  
      ),  
      FlatButton(  
        child: Text(localizations.okButtonLabel),  
        onPressed: () {  
          int amount = int.tryParse(dateTextController.text);  
          Navigator.pop<int>(context, amount);  
        },  
      ),  
    ];  

dialog.dartのこの部分、どちらのボタンを押してもNavigator.popするのだが、引数の数が違う。第二引数には返却したい値を指定することができるようなので、int変換した値を設定する。

      onPressed: () {  
        var result = showDepositDialog(context: context);  
        print(result);  
      },  

depositButton.dartのonPressedを書き換えて、ダイアログが戻り値を受け取るようにする。そしてデバッグ表示させる。
残念ながらデバッグコンソールにprint文は表示されなかった。

ダイアログの戻り値が表示されない理由

実は、デバッグ文には、入金ボタンを押したときに気になる1行が表示されていた。

Restarted application in 1,731ms.
I/flutter ( 349): Instance of 'Future'

showDepositDialogの戻り値がFuture型であったことを思い出す。これは非同期処理の際に用いる未来型(?)の型定義になる。つまり、上にあげたprint文では非同期処理の結果を受け取れていないことになる。

      onPressed: () async {  
        var result = await showDepositDialog(context: context);  
        print(result);  
      },  

Restarted application in 1,685ms.
I/flutter ( 349): 100

これで戻り値を受け取ることができた。さらに、Future云々の1行も消えている。

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

そろそろ業界からオプトアウトされそうな枯れ老人の足掻きブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう