シングルトンによるデータストアの実装

アプリケーションワイドにデータを持つシングルトンの作成方法を紹介します。

Dart にはシングルトン作成用の仕組みがあります。コンストラクタに factory とマークすることによって、 オブジェクトを常に作成するのではなく、もしキャッシュがあればそれを使う、という動作を実装できます。

item_store.dart として次を作成します。

class ItemStore {
  static final Map<dynamic, dynamic> _item = <dynamic, dynamic>{};
  static final ItemStore _cache = ItemStore._internal();
  
  factory ItemStore() {
    return _cache;
  }
  
  ItemStore._internal();

  set(dynamic key, dynamic value) => _item[key] = value;
  get(dynamic key) => _item[key];
}

ここでは _cache という変数に、一度作成したオブジェクト (インスタンス) を保持しており、 factory とマークされたコンストラクタではそれを常に返しています。

これを利用する側は、これがシングルトンであるかどうかを意識することなく、通常通りコンストラクタを呼び出すことで、 毎回同じオブジェクトを利用することができます。

_internal() としている箇所は、コンストラクタをプライベートアクセスにしているところです。 _internal() という名前は慣例的なものであって、必ずしもこの名前である必要はありません。極端な話 _() などでも構いません。

ここで作成するテストプログラムは次のようになります。

最初の画面 HomePage で名前を入力したら、次の画面でそれを表示しています。

このスクリーン遷移をまたいでデータを受け渡しするのに、シングルトンで実装したデータストアを利用しています。

最初のページは次のような実装になります。

pages/home.dart

import 'package:flutter/material.dart';
import 'package:test30_globalstore/item_store.dart';

class HomePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _HomePageState();
  }
}

class _HomePageState extends State<HomePage> {
  TextEditingController _controller;

  @override
  void initState() {
    _controller = TextEditingController();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home'),
      ),
      body: Container(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            TextField(
              controller: _controller,
              decoration: InputDecoration(
                labelText: 'Username',
                icon: Icon(Icons.account_box),
              ),
            ),
            RaisedButton(
              child: Text('Next'),
              onPressed: () {
                ItemStore().set('name', _controller.text);
                Navigator.of(context).pushNamed('/second');
              },
            ),
          ],
        ),
      ),
    );
  }
}

二番目の画面を pages/second.dart として次とします。

import 'package:flutter/material.dart';

import '../item_store.dart';

class SecondPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _SecondPageState();
  }
}

class _SecondPageState extends State<SecondPage> {
  String _name;

  @override
  void initState() {
    _name = ItemStore().get('name');
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Second'),
      ),
      body: Center(
        child: Container(
          padding: EdgeInsets.all(16),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Padding(
                padding: EdgeInsets.all(20),
                child: Text(
                  'Hello, $_name!',
                  style: TextStyle(fontSize: 20),
                ),
              ),
              RaisedButton(
                child: Text('Back'),
                onPressed: () {
                  Navigator.of(context).pop();
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

この上で、main.dart を次とします。

main.dart

import 'package:flutter/material.dart';
import './pages/home.dart';
import './pages/second.dart';

void main() => runApp(
      MaterialApp(
        debugShowCheckedModeBanner: false,
        home: HomePage(),
        routes: <String, WidgetBuilder>{
          '/home': (BuildContext context) => HomePage(),
          '/second': (BuildContext context) => SecondPage()
        },
      ),
    );

以上、ここでは Dart でシングルトンを実装する方法と、それを利用する方法について説明しました。

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Flutter 入門