初心者と学ぶFlutter第6回:ローカルデータの保存

初心者と学ぶFlutter

はじめに

前回はToDoリストを作成しました。

初心者と学ぶFlutter第5回:Todoアプリの設計と実装
FlutterでシンプルなTodoアプリを作成。ScaffoldやListTileなどのUI要素を使い、CRUD機能を実装してタスク管理を学びます。

しかしこのままではアプリを起動するたびにデータが消えてしまいます。
そこで今回はローカルデータの保存について学びます。

アプリケーション開発をしていると、ローカルデータの保存は重要な機能のひとつです。たとえば、アプリを終了した後でも、ユーザーの設定やデータを保持したい場合、ローカルにデータを保存する仕組みが必要になります。Flutterでは、shared_preferencesパッケージを使うことで、簡単にデータを端末に保存し、アプリを再起動してもデータを保持することができます。

この記事では、shared_preferencesを使ってデータをローカルに保存する方法を解説し、これを使ってTodoアプリにデータの永続化を実装します。

shared_preferencesパッケージのインストール

まずは、shared_preferencesパッケージをプロジェクトにインストールします。これは、Flutterの公式ライブラリであり、簡単にローカルデータを保存・読み込みできる仕組みを提供します。

パッケージのインストール

pubspec.yamlファイルにshared_preferencesを追加します。

YAML
dependencies:
  flutter:
    sdk: flutter
  shared_preferences: ^2.0.6

追加後、以下のコマンドを実行してインストールします。

Bash
flutter pub get

これで、shared_preferencesパッケージがプロジェクトに追加され、使用できるようになりました。

shared_preferencesの基本的な使い方

shared_preferencesは、基本的にキーと値のペアでデータを保存します。数値や文字列、ブール値、リストなどの簡単なデータ型を保存することができます。

データの保存

shared_preferencesにデータを保存するには、次のコードを使います。

Dart
import 'package:shared_preferences/shared_preferences.dart';

Future<void> saveData(String key, String value) async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setString(key, value);
}

setStringメソッドで、文字列データを保存しています。数値の場合はsetInt、ブール値の場合はsetBoolなど、データ型に応じたメソッドが用意されています。

データの読み込み

保存したデータを読み込む方法は以下の通りです。

Dart
Future<String?> loadData(String key) async {
  final prefs = await SharedPreferences.getInstance();
  return prefs.getString(key);  // キーに対応するデータを取得
}

データを読み込むときは、保存したときと同じキーを使って取得します。

データの削除

保存したデータを削除することもできます。

Dart
Future<void> removeData(String key) async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.remove(key);
}

Todoアプリにデータの永続化を実装

これまでの知識を基に、前回作成したTodoアプリに、shared_preferencesを使ってデータの永続化を追加していきます。これにより、アプリを再起動してもTodoリストが保持されるようになります。

Todoリストの保存と読み込み

まず、Todoリストをshared_preferencesに保存し、アプリ起動時にリストを読み込むようにします。以下は、Todoアイテムを保存・読み込む処理です。

Todoリストの保存

Dart
  Future<void> _saveTodoList() async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setStringList('todo_list', _todoItems);
  }

setStringListを使うことで、Todoアイテムのリストを保存します。

Todoリストの読み込み

Dart
  Future<void> _loadTodoList() async {
    final prefs = await SharedPreferences.getInstance();
    setState(() {
      _todoItems = prefs.getStringList('todo_list') ?? [];
    });
  }

Todoアプリに保存機能を統合

次に、Todoアプリの保存機能を追加します。以下の例では、Todoリストの追加・削除時に自動的にデータを保存し、アプリ起動時にリストを読み込むようにしています。

アプリ起動時にデータを読み込む

initStateでアプリ起動時に_loadTodoListを呼び出し、保存されたTodoリストを読み込みます。

Dart
  @override
  void initState() {
    super.initState();
    _loadTodoList();  // アプリ起動時にデータを読み込む
  }

データの追加・更新・削除

Todoリストにタスクを追加・更新・削除した際に、保存するように修正します。

Dart
// 追加
void _addTodoItem(String task) {
  if (task.isNotEmpty) {
    setState(() => _todoItems.add(task));
    _saveTodoList();
  }
}

// 更新
void _editTodoItem(int index, String newTask) {
  if (newTask.isNotEmpty) {
    setState(() => _todoItems[index] = newTask);
    _saveTodoList();
  }
}

// 削除
void _removeTodoItem(int index) {
  setState(() => _todoItems.removeAt(index));
  _saveTodoList();
}

完成したコード例

以下は、shared_preferencesを使ったTodoアプリの完成版コードです。アプリを終了してもTodoリストが保持され、再起動してもデータが消えないことを確認できます。

Dart
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() {
  runApp(TodoApp());
}

class TodoApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Todo App',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: TodoListScreen(),
    );
  }
}

class TodoListScreen extends StatefulWidget {
  @override
  _TodoListScreenState createState() => _TodoListScreenState();
}

class _TodoListScreenState extends State<TodoListScreen> {
  List<String> _todoItems = [];

  @override
  void initState() {
    super.initState();
    _loadTodoList();
  }

  Future<void> _saveTodoList() async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setStringList('todo_list', _todoItems);
  }

  Future<void> _loadTodoList() async {
    final prefs = await SharedPreferences.getInstance();
    setState(() {
      _todoItems = prefs.getStringList('todo_list') ?? [];
    });
  }

  void _addTodoItem(String task) {
    if (task.isNotEmpty) {
      setState(() => _todoItems.add(task));
      _saveTodoList();
    }
  }

  void _removeTodoItem(int index) {
    setState(() => _todoItems.removeAt(index));
    _saveTodoList();
  }

  void _editTodoItem(int index, String newTask) {
    if (newTask.isNotEmpty) {
      setState(() => _todoItems[index] = newTask);
      _saveTodoList();
    }
  }

  void _promptAddTodoItem() {
    TextEditingController _textFieldController = TextEditingController();
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text('Add a new task'),
          content: TextField(
            controller: _textFieldController,
            decoration: InputDecoration(hintText: 'Enter task here'),
          ),
          actions: <Widget>[
            TextButton(
              child: Text('Cancel'),
              onPressed: () => Navigator.of(context).pop(),
            ),
            TextButton(
              child: Text('Add'),
              onPressed: () {
                _addTodoItem(_textFieldController.text);
                Navigator.of(context).pop();
              },
            ),
          ],
        );
      },
    );
  }

  void _promptEditTodoItem(int index) {
    TextEditingController _textFieldController = TextEditingController();
    _textFieldController.text = _todoItems[index];

    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text('Edit task'),
          content: TextField(
            controller: _textFieldController,
            decoration: InputDecoration(hintText: 'Edit task here'),
          ),
          actions: <Widget>[
            TextButton(
              child: Text('Cancel'),
              onPressed: () => Navigator.of(context).pop(),
            ),
            TextButton(
              child: Text('Save'),
              onPressed: () {
                _editTodoItem(index, _textFieldController.text);
                Navigator.of(context).pop();
              },
            ),
          ],
        );
      },
    );
  }

  Widget _buildTodoList() {
    return ListView.builder(
      itemCount: _todoItems.length,
      itemBuilder: (context, index) {
        return _buildTodoItem(_todoItems[index], index);
      },
    );
  }

  Widget _buildTodoItem(String todoText, int index) {
    return ListTile(
      title: Text(todoText),
      trailing: IconButton(
        icon: Icon(Icons.delete),
        onPressed: () => _removeTodoItem(index),
      ),
      onTap: () => _promptEditTodoItem(index),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Todo List')),
      body: _buildTodoList(),
      floatingActionButton: FloatingActionButton(
        onPressed: _promptAddTodoItem,
        child: Icon(Icons.add),
      ),
    );
  }
}

まとめ

この記事では、shared_preferencesを使ってデータをローカルに保存し、アプリを再起動してもTodoリストが保持される仕組みを実装しました。shared_preferencesは、設定や小さなデータを保存するために非常に便利な方法です。

この記事のポイント

  • shared_preferencesを使ったローカルデータの保存方法を学習。
  • データの保存、読み込み、削除方法を理解。
  • Todoアプリに永続化機能を追加し、アプリを再起動してもデータが保持されるように実装。

次回はテストに関して学びます。

次回はこちら

初心者と学ぶFlutter第7回:テスト
Flutterで単体テストとウィジェットテストの基本を学び、アプリの品質向上を目指します。実践的なテストの書き方を解説!

コメント

タイトルとURLをコピーしました