Widgetの組み立てを関数化する

2023年2月7日

開発環境
OS:Windows 10
SDK:VS Code + flutter 3.3.10

概要

Widgetの組み立ては関数化したほうが可読性が上がる。

flutterのWidget構成はどうしてもネストが深くなる

flutterではWidgetを組み合わせて画面を作成します。

Widgetによる画面構築は感覚的にも分かりやすくとても簡単に画面を作成できます。

ただ一つ、Widgetを組み合わせていると問題が。

WidgetのなかにWidgetを入れ、そのなかに更にWidgetを追加して・・・

と、Widgetを組み合わせていくとネストが深くなるんです。

その結果、コードの可読性が落ちてしまい、

ぱっと見て画面構成が把握できない難解なコードとなってしまうわけです。

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
       width: double.infinity,
       height: double.infinity,
       alignment: Alignment.center,
       child: Column(children: [
         Expanded(
           flex: 1,
           child: Row(children: [
             Expanded(
               flex: 3,
               child: Column(children: [
                 Expanded(flex: 1, child: Container(alignment: Alignment.center, child:Text())),
                 Expanded(flex: 19, child: ListView.builder(
                     itemCount: droplist.length,
                     itemBuilder: (context, index) {
                       return InkWell(
                         onTap: () async {},
                         child: Container(
                           width: double.infinity,
                           alignment: Alignment.center,
                           decoration: BoxDecoration(),
                           child: Stack(
                             alignment: Alignment.bottomCenter,
                             children: [
                               Container()
                           ],)
                         ),);
                     },
                   ))
               ],)
             ),
             Expanded(
               flex: 7,
               child: Stack(children: [
                 GoogleMap(),
                 Container()
               ])
             ),
           ]),),
         Container(height: adHeight)
       ],)
     )
    );
  }

Widgetは関数化できる

そこでどうするか。

Widgetの構成を関数化し分割します。

Widgetを戻り値とする関数として定義することができるのです。

例えば先ほどのmap.dartの場合、関数化してみると。

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        width: double.infinity,
        height: double.infinity,
        alignment: Alignment.center,
        child: _createMainView()
      )
    );
  }
 //メインビュー
 Widget _createMainView() {
   return Column(children: [
      Expanded(
        flex: 1,
        child: Row(children: [
          Expanded(
            flex: 3,
            child: _createListView()
          ),
          Expanded(
            flex: 7,
            child: _createMapView()
          ),
        ]),),
      Container(height: adHeight)
      ],)
    );
  }

  //画面サイドビュー
  Widget _createListView() {
    return Column(children: [
      Expanded(flex: 1, child: Container(alignment: Alignment.center, child:Text())),
      Expanded(flex: 19, child: ListView.builder(
          itemCount: droplist.length,
          itemBuilder: (context, index) {
            return InkWell(
              onTap: () async {},
              child: Container(
                width: double.infinity,
                alignment: Alignment.center,
                decoration: BoxDecoration(),
                child: Stack(
                  alignment: Alignment.bottomCenter,
                  children: [
                    Container()
                ],)
              ),);
          },
        ))
    ],);
  }
  
  //マップビュー
  Widget _createMapView() {
    return Stack(children: [
      GoogleMap(),
      Container()
    ]);
  }

関数はbuild処理の前に定義しておけば大丈夫です。

どうでしょうか?

結局のところ、Widgetの数自体は変わらないのでステップ数が減ることはないですが、可読性は上がったのではないでしょうか?

Widgetはクラス化もできる

関数化して満足していたらクラス化もできたようです。

class MapSideView extends StatelessWidget {
    const MapSideView({Key? key}) : super(Key key);

    @override
    Widget build(BuildContext context) {
    return Column(children: [
      Expanded(flex: 1, child: Container(alignment: Alignment.center, child:Text())),
      Expanded(flex: 19, child: ListView.builder(
          itemCount: droplist.length,
          itemBuilder: (context, index) {
            return InkWell(
              onTap: () async {},
              child: Container(
                width: double.infinity,
                alignment: Alignment.center,
                decoration: BoxDecoration(),
                child: Stack(
                  alignment: Alignment.bottomCenter,
                  children: [
                    Container()
                ],)
              ),);
          },
        ))
    ],);
    }
  }

クラス化の利点はsetState()などの再描画においてリフレッシュの範囲をクラス単位で管理できる点のよう。

また、flutter公式もクラス化を推奨しているようです。

CSVファイルの文字化け対策

2023年2月5日

開発環境
OS:Windows 10
SDK:VS Code + flutter 3.3.10

概要

flutterでCSVデータを出力するときはBOMを付けた方がいい。

エクセルはShift-JISに対応している

flutterでCSVを出力します。

テキストエディタで表示すると問題なし。

いざ、エクセルで開いてみた結果。。。

文字化けしています。

なぜなのか調べてみたところ、エクセルは文字コードをShift-JISで取り扱うようです。

それに対し、flutterはutf-8で出力します。

故に文字コードが異なるため、文字化けが発生したということですね。

BOMを付ける

対策はどうするのか。

flutterでutf-8をshift-jisに変換して出力すればいいんじゃないのか。

最初にそう思ったわけですが、「文字コード変換」と「変換後のデータ出力」。

2つの対応が必要になります。

もっとうまい方法がないのかとネットで検索してみると見つけました。

エクセルはBOM付きutf-8なら表示できる。

つまり、

utf-8のデータの先頭にBOMと呼ばれる識別コードを付与するだけです。

これなら簡単。

改修したコードがこちら。

//CSV生成
final csv = await makeFileCsv();
// utf-8バイト変換
final excelCsvBytes = [0xEF, 0xBB, 0xBF, ...utf8.encode(csv)];
// base64エンコード変換
final base64ExcelCsvBytes = base64Encode(excelCsvBytes);
AnchorElement(
    href: 'data:text/plain;charset=utf-8;base64,$base64ExcelCsvBytes')
  ..setAttribute('download', 'test.csv')
  ..click();

2行目でCSVデータをバイト配列に変換して先頭に「0xEF 0xBB 0xBF」を付与。

3行目でそのバイト配列をbase64に再変換。

あとはそのデータを「utf-8;base64」として出力する。

という流れです。

改修コードで確認した結果、テキストエディタでも問題なく開き、エクセルの文字化けもなくなりました。

constを付ける意味

2023年1月23日

開発環境
OS:Windows 10
SDK:VS Code + flutter 3.3.10

概要

constは極力、付けた方がいい。

青い波線に気付く

最近、flutterでWebサイトを作っているのですが、何も問題がなさそうな箇所に青い波線が出ているのに気付きました。

カーソルを青い波線に合わせてみると。

「定数コンストラクタにはconstを付与するのが好ましい。」

とのポップアップが。

constの役割

画面を構成するパーツ(flutterで言うところのwidget)には固定文言などの静的表示を行うパーツと入力データなどのその都度値が変わる動的表示を行うパーツが存在します。

constはこの静的表示を行うWidgetに対し、宣言するものとのこと。

そうすることによってコンパイル時(アプリ生成時)に値を確定させることでき、

画面表示時の処理負荷を軽減させる=画面描画速度を上げる

ことができるようです。

constの定義


Widget(Column)内のWidget(Text)がすべてconstとなるような場合、

上位のWidget側にconstを定義するようなパターンもあります。

常にconstを付与に対しケアしたほうがいいですが、ワーニングで知らせてくれるので助かります。

VS Code便利ですね。