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公式もクラス化を推奨しているようです。