Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

FlutterのStreamBuilderをつかって無駄な再描画を減らそう

More than 1 year has passed since last update.

お題

ポップアップで選択して、選択結果を表示する画面を考えます。

完成イメージ

未選択時
時刻を選択
選択後

StatefulWidgetsetStateを使う

class HomeWidget extends StatefulWidget {
  @override
  HomeState createState() => HomeState();
}
class HomeState extends State<HomeWidget> {
  TimeOfDay _time;

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Container(
          decoration: BoxDecoration(
            border: Border(
              bottom: BorderSide(width: 3),
            ),
          ),
          child: Text(
            _time == null ? "未選択" : _time.format(context),
            style: TextStyle(fontSize: 50),
          ),
        ),
        RaisedButton(
          child: Text(
            "時刻を選択する",
            style: TextStyle(fontSize: 20),
          ),
          color: Colors.deepOrangeAccent,
          onPressed: () async {
            final select = await showTimePicker(
              context: context,
              initialTime: TimeOfDay.now(),
            );
            this.setState(() => _time = select);
          },
        ),
      ],
    );
  }
}

この例だと、ボタンを押して時刻を選択すると、テキストだけではなく、ボタンも再描画されています。

StreamBuilderを使う

Stream?

Streamのイメージは、蛇口だ。
sinkにデータを流すと、streamに流れるので、listenで捕まえて処理するのだ。

長めだけどたぶんわかりやすいBLoCパターンの解説

StreamBuilderを使って書き換えよう

class HomeWidget extends StatefulWidget {
  @override
  HomeState createState() => HomeState();
}

class HomeState extends State<HomeWidget> {
  final _onTimeChange = StreamController<TimeOfDay>();

  @override
  void dispose() {
    // StreamControllerは必ず開放する
    _onTimeChange.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        // 2つのWidgetは親のもつStreamControllerのstreamとsinkを使ってデータの受け渡しを行う
        TimeText(stream: _onTimeChange.stream),
        TimeSelector(sink: _onTimeChange.sink),
      ],
    );
  }
}

/// 時刻を受け取って表示する
class TimeText extends StatelessWidget {
  /// 受け口
  final Stream<TimeOfDay> stream;
  TimeText({this.stream});

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        border: Border(
          bottom: BorderSide(width: 3),
        ),
      ),
      child: StreamBuilder(
        // 指定したstreamにデータが流れてくると再描画される
        stream: this.stream,
        builder: (BuildContext context, AsyncSnapshot<TimeOfDay> snapShot) {
          // StreamControllerから流れてきたデータを使って再描画
          return Text(
            snapShot.hasData ? snapShot.data.format(context) : "未選択",
            style: TextStyle(fontSize: 50),
          );
        },
      ),
    );
  }
}

/// 時刻を選択して、選択結果を渡す
class TimeSelector extends StatelessWidget {
  /// 渡し口
  final StreamSink<TimeOfDay> sink;
  TimeSelector({this.sink});

  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      child: const Text(
        "時刻を選択する",
        style: TextStyle(fontSize: 20),
      ),
      color: Colors.deepOrangeAccent,
      onPressed: () async {
        final select = await showTimePicker(
          context: context,
          initialTime: TimeOfDay.now(),
        );
        // sinkに選択した時刻を流す
        this.sink.add(select);
      },
    );
  }
}

StreamBuilderは指定したstreamにデータが流れてくると、自動で再描画が実行されます。

今回の例だと、時刻を選択すると、選択したデータ<TimeOfDay>sinkを通して流れ込み、StreamBuildersnapShot.dataに流れ着きます。

ボタンやその他のWidgetを再描画することなく、テキストだけを再描画することができます。

また、ボタンからテキストへはStreamControllerを通してデータの受け渡しがされているため、
StreamControllerさえ両方のWidgetに渡してしまえば、うまくデータのやり取りが可能です。

こうすることで、無駄な再描画を抑えつつ、状態管理の煩雑さから開放され、各Widgetをうまくパーツ化、再利用することができるようになります。

みんなもStreamBuilderをうまく使って快適なFlutterライフを!!!!

sgmryk
普段はPHP使いです。 最近はHaskellがマイブーム。モナモナ。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away