Re:birth エンジニアリング

Flultterとテックブログと時々iOS

Flutter のState クラスについて勉強する

今回はFlutter の状態を扱う機能であるState クラスについて勉強します。

これまで書いてきた StateクラスはStatelessWidget(静的クラス)でした。 Flutter のState は2種類存在します。

  • StatelessWidget (静的なState)
  • StatefulWidget (動的なState)

よくFlutter の基本的なソースコードである

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  String title = 'Flutter のテストアプリ';
  String message = 'Hello World, Flutter';
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      debugShowCheckedModeBanner: false,
    );
  }
}

で使われるときがStatelessWidget で考えるような感じです。 それに対して変数によって値を変えたいときに使うウィジェットの場合にはStatefulなウィジェットを継承させてクラスを作成します。

StatefulWidget の基本形な次のとおりです。

// ウィジェット
class AWidget extends StatefulWidget {
  _AWidgetState createState() => new _AWidgetState();
}

// 状態を持つクラス
class _AWidgetState extends State<AWidget> {
  @override
  Widget build(BuildContext context) {
  }
}

と2つのクラスを作成しなければいけない感じです。 この基本形は丸暗記すればよさそう。

このStatefulWidgetを使ったサンプルコードを下に記載します。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  String title = 'Flutter のテストアプリ';
  String message = 'Hello World, Flutter';
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter App',
      home: new SamplePage(
        title: this.title,
        message: this.message,
      ),
    );
  }
}

// ウィジェット
class SamplePage extends StatefulWidget {
  String title;
  String message;

  SamplePage({this.title, this.message}): super();
  _SamplePageState createState() => new _SamplePageState();
}

// 状態を持つクラス
class _SamplePageState extends State<SamplePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.message),
      ),
      body: Text(widget.message,
      style: TextStyle(fontSize: 32.0),
      ),
    );
  }
}

ぱっと見ではネストが深いイメージがあります。 これが最小単位でビルドすると下のスクリーンショットの画像が表示されます。

f:id:qed805:20200115224336p:plain
StatefulWidgetを使った最小コード

Widgetクラスで別ファイルに切り分ける

本当はこのまま下にStateクラスを作成して入れ子に組み込んでいけばレイアウトができてくるのですが、 私の場合まだ慣れないのでここでSamplePageを別ファイルに分離して切り分けたいと思います。

Flutter の場合、新しいDartファイルを作成してそこに移動させたらいいみたいです。

f:id:qed805:20200115224847p:plain

このようにlibディレクトリの中に新しいdartクラスsample_widgetファイルを作成しました。

このsample_widget のファイルに下のソースコードを書きます。

import 'package:flutter/material.dart';

// ウィジェット
class SamplePage extends StatefulWidget {
  String title;
  String message;

  SamplePage({this.title, this.message}): super();
  _SamplePageState createState() => new _SamplePageState();
}

// 状態を持つクラス
class _SamplePageState extends State<SamplePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.message),
      ),
      body: Text(widget.message,
        style: TextStyle(fontSize: 32.0),
      ),
    );
  }
}

f:id:qed805:20200115225026p:plain

そして、main.dart のファイルを次のように編集します。 といっても、sample_widgetファイルを読み込むためにimport しただけです。

import 'package:flutter/material.dart';
import 'package:practice_app/sample_page.dart';

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

class MyApp extends StatelessWidget {
  String title = 'Flutter のテストアプリ';
  String message = 'Hello World, Flutter';
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter App',
      home: new SamplePage(
        title: this.title,
        message: this.message,
      ),
    );
  }
}

これで作成したStatefulWidget を別ファイルからimportして使えるようになりました。 まだFlutte 触りだして1週間ですのでクラス名も分離の基準もこれで正しいか分かりません。

ですが、これでUIと画面を分離できたかなと思います。

Stateのプロパティにアクセスするwidgetプロパティ

ここで説明を省略しましたが、Stateクラスで使われているwidgetというものがあります。 widget.titleとかwidget.messageと書かれている箇所です。 widgetとはStateクラスに用意されているプロパティで_SamplePageStateそのものを指します。

import 'package:flutter/material.dart';

// 状態を持つクラス
class _SamplePageState extends State<SamplePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.message),
      ),
      body: Text(widget.message,
        style: TextStyle(fontSize: 32.0),
      ),
    );
  }
}

簡単に言えば、Stateを継承しているのでこのStateを継承することで widget プロパティ(つまり、this)にアクセスできる仕様なんだと思います。

とりあえず納得するしかありません。

これでだいたいStateについて学べたかと思います。