25
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Flutterで "名前付きコンストラクタ" と "リダイレクトコンストラクタ" でウィジェットを作る例

Posted at

Dartには色々なコンストラクタがありますが、あまりFlutterで使いこなせていない感がありました。

同僚と話していて、こういった使い方なら汎用性があるのでは?というのがあったので、「名前付きコンストラクタ」と「リダイレクトコンストラクタ」を使って説明したいと思います。

完成のイメージ

  • メンバー一覧用のカードを用意したい
  • 退会したメンバーは背景をグレーにして、「退会済み」の文言を表示したい
スクリーンショット 2019-11-23 17.33.36.png

元となるカードのウィジェットを作成する

メンバー個々のウィジェットを作成する際に下記のデータを送ってもらうこととします。

  • メンバーの名前
  • アバター画像のファイル名
  • 在籍状況
  • 背景色

ウィジェットのコードはこんな感じになると思います。

main.dart

Widget build(BuildContext context) {
  return Card(
    child: ListTile(
      //アバター画像
      leading: ClipRRect(
        borderRadius: BorderRadius.circular(25),
        child: Image.asset(
          'images/${widget.imgFileName}',
          width: 
          height: 50,
        ),
      ),
      title: Text(widget.name), //名前
      trailing: widget.statusIcon, //在籍状況
    ),
    color: widget.backgroundColor, //背景色
    margin: EdgeInsets.symmetric(vertical: 20),
  );
}

名前付きコンストラクタ で呼び出す

通常のメンバーカードであるnormalと、退会済みのメンバーカードのdisabledをそれぞれ指定することでカードの作り分けを行いたいので、コードは以下のようにしたいです。

main.dart
Column(
  children: <Widget>[
    MemberCard.normal(name: '鈴木 たかし', imgFileName: 'avator1.png'),
    MemberCard.disabled(name: '高橋 ゆうさく', imgFileName: 'avator2.png'),
    MemberCard.normal(name: 'Sarah Adams', imgFileName: 'avator3.jpeg'),
    MemberCard.normal(name: 'Richard Mason', imgFileName: 'avator4.jpeg'),
  ]
)

このnormalと、disabledの部分が名前付きコンストラクタになります。

名前付きコンストラクタ を作成する

実際のコンストラクタは以下のようになります。

main.dart
class MemberCard extends StatefulWidget {
  final String name; //名前
  final String imgFileName; //アバター画像

  // 通常のユーザー
  MemberCard.normal({name, imgFileName})
      : this._init(
        name: name,
        imgFileName: imgFileName
      );

  // 退会したユーザー
  MemberCard.disabled({name, imgFileName})
      : this._init(
        name: name,
        imgFileName: imgFileName
  );

  MemberCard._init({Key key,
    this.name = '',
    this.imgFileName = ''
  }) : super(key: key);

  @override
  _MemberCardState createState() => _MemberCardState();
}

コンストラクタの引数は宣言時と同じものです。
その後「:」(コロン)をつけて、this._initに追加のコンストラクタ処理を渡しています。
これが、リダイレクトコンストラクタになります。

実際のthis._initを見てみます。
keyは自動的に振り当てられたもので、nameimgFileNameは引数として渡されたものです。

そしてまたそれを親クラスにsuperとして処理を渡しています。

しかし、これだけではnormaldisabledの違いがなく、あまり恩恵を感じられませんので、ひと手間加えてみます。

明確に目的を分けて作成する

main.dart
class MemberCard extends StatefulWidget {
  final String name; //名前
  final String imgFileName; //アバター画像
  final Color backgroundColor; //背景色(追加部分)
  final statusIcon; //メンバーのステータス(追加部分)

  // 通常のユーザー
  MemberCard.normal({name, imgFileName})
      : this._init(
        name: name,
        imgFileName: imgFileName,
        statusIcon: Icon(Icons.check_circle, color: Colors.green,) //(追加部分)
      );

  // 退会したユーザー
  MemberCard.disabled({name, imgFileName})
      : this._init(
      name: name,
      imgFileName: imgFileName,
      backgroundColor: Colors.black12, //(追加部分)
      statusIcon: Text('退会済み') //(追加部分)
  );

  MemberCard._init({Key key,
    this.name = '',
    this.imgFileName = '',
    this.backgroundColor = Colors.white, //(追加部分)
    this.statusIcon = null //(追加部分)
  }) : super(key: key);

  @override
  _MemberCardState createState() => _MemberCardState();
}

//(追加部分)」とコメントしている部分が追加したコードの差分です。
ポイントとなる部分を説明します。

backgroundColor

新しいインスタンス変数としてbackgroundColorを追加しました。
それぞれのコンストラクタで初期化されていたり、されていなかたりします。

  • normal : 初期化していない
  • disabled : 初期化している
  • init : 初期化している

つまり、normalで初期化していないbackgroundColorinitで初期化していることになります。

statusIcon

インスタンス変数statusIconには型が付いておらず、各コンストラクタでは、下記のように初期化しております。

  • normal : Icon()
  • disabled : Text()
  • init : null

あまり綺麗なやり方ではないと思いますが、型を指定しないことによって自由に初期化にウィジェットを当てることができます。

プログラム全文

main.dart
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Constructor Demo',
      home: Scaffold(
          appBar: AppBar(
            title: Text('メンバーリスト'),
          ),
          body: Column(
            children: <Widget>[
              MemberCard.normal(name: '鈴木 たかし', imgFileName: 'avator1.png'),
              MemberCard.disabled(name: '高橋 ゆうさく', imgFileName: 'avator2.png'),
              MemberCard.normal(name: 'Sarah Adams', imgFileName: 'avator3.jpeg'),
              MemberCard.normal(name: 'Richard Mason', imgFileName: 'avator4.jpeg'),
            ],
          )

      ),
    );
  }
}

class MemberCard extends StatefulWidget {
  final String name; //名前
  final String imgFileName; //アバター画像
  final Color backgroundColor; //背景色
  final statusIcon; //メンバーのステータス

  // 通常のユーザー
  MemberCard.normal({name, imgFileName})
      : this._init(
        name: name,
        imgFileName: imgFileName,
        statusIcon: Icon(Icons.check_circle, color: Colors.green,)
      );

  // 退会したユーザー
  MemberCard.disabled({name, imgFileName})
      : this._init(
      name: name,
      imgFileName: imgFileName,
      backgroundColor: Colors.black12,
      statusIcon: Text('退会済み')
  );

  MemberCard._init({Key key,
    this.name = '',
    this.imgFileName = '',
    this.backgroundColor = Colors.white,
    this.statusIcon = null
  }) : super(key: key);

  @override
  _MemberCardState createState() => _MemberCardState();
}

class _MemberCardState extends State<MemberCard> {
  @override
  Widget build(BuildContext context) {
    return Card(
      color: widget.backgroundColor,
      margin: EdgeInsets.symmetric(vertical: 20),
      child: ListTile(
        leading: ClipRRect(
          borderRadius: BorderRadius.circular(25),
          child: Image.asset(
            'images/${widget.imgFileName}',
            width: 50,
            height: 50,
          ),
        ),
        title: Text(widget.name),
        trailing: widget.statusIcon,
      ),
    );
  }
}

プログラム全文(github)
https://github.com/naoaki-kaito/flutter_constructor_example

25
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?