[基礎知識]RiverPodによる状態管理の手順

Riverpodを使ったタスク管理アプリケーションのサンプルコード

1.ファイル構成と役割

pubspec.yaml

  • 役割: アプリケーションで使用するパッケージを管理するファイル。
  • 自動生成: flutter create <project_name>コマンドを実行すると生成されます。

lib/main.dart

  • 役割: アプリケーションのエントリーポイント。Riverpodの設定を行います。

lib/models/task.dart

  • 役割: タスクモデルを定義するファイル。

lib/providers/task_provider.dart

  • 役割: Riverpodのプロバイダを定義するファイル。

lib/screens/home_screen.dart

  • 役割: タスクのリストと追加ボタンを表示するホーム画面。

lib/screens/add_task_screen.dart

  • 役割: 新しいタスクを追加する画面。

lib/screens/edit_task_screen.dart

  • 役割: 既存のタスクを編集する画面。

lib/widgets/task_list.dart

  • 役割: タスクリストを表示するウィジェット。

2.サンプルアプリ

3.サンプルコード

(1)pubspec.yaml

必要なパッケージ(flutter_riverpod)を追加します。

Dart
name: task_management_app
description: A new Flutter project using Riverpod for state management.
publish_to: 'none'
version: 1.0.0+1

environment:
  sdk: ">=2.12.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  flutter_riverpod: ^1.0.0
  cupertino_icons: ^1.0.2

dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:
  uses-material-design: true

(2)lib/main.dart

アプリケーションのエントリーポイントを設定します。

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

import 'screens/home_screen.dart';

void main() {
  runApp(
    const ProviderScope(
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Task Management App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const HomeScreen(),
    );
  }
}

(3)lib/models/task.dart

タスクモデルを定義します。

Dart
class Task {
  String title;
  String description;
  bool isCompleted;

  Task({
    required this.title,
    required this.description,
    this.isCompleted = false,
  });
}

(4)lib/providers/task_provider.dart

タスクの状態を管理するプロバイダを定義します。

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

import '../models/task.dart';

// StateNotifierでタスクのリストを管理します
class TaskList extends StateNotifier<List<Task>> {
  TaskList() : super([]);

  void addTask(Task task) {
    state = [...state, task];
  }

  void editTask(int index, Task task) {
    state = [
      for (int i = 0; i < state.length; i++)
        if (i == index) task else state[i]
    ];
  }

  void removeTask(int index) {
    state = [
      for (int i = 0; i < state.length; i++)
        if (i != index) state[i]
    ];
  }

  void toggleTaskCompletion(int index) {
    state = [
      for (int i = 0; i < state.length; i++)
        if (i == index)
          Task(
            title: state[i].title,
            description: state[i].description,
            isCompleted: !state[i].isCompleted,
          )
        else
          state[i]
    ];
  }
}

// TaskListプロバイダを定義します
final taskListProvider = StateNotifierProvider<TaskList, List<Task>>((ref) {
  return TaskList();
});

(5)lib/screens/home_screen.dart

ホーム画面を作成します。

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

import '../widgets/task_list.dart';
import 'add_task_screen.dart';

class HomeScreen extends ConsumerWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Task Management'),
      ),
      body: const TaskList(),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => AddTaskScreen()),
          );
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

(6)lib/screens/add_task_screen.dart

タスク追加画面を作成します。

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

import '../models/task.dart';
import '../providers/task_provider.dart';

class AddTaskScreen extends ConsumerWidget {
  final _titleController = TextEditingController();
  final _descriptionController = TextEditingController();

  AddTaskScreen({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Add Task'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextField(
              controller: _titleController,
              decoration: const InputDecoration(labelText: 'Title'),
            ),
            TextField(
              controller: _descriptionController,
              decoration: const InputDecoration(labelText: 'Description'),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                final title = _titleController.text;
                final description = _descriptionController.text;
                if (title.isNotEmpty && description.isNotEmpty) {
                  ref.read(taskListProvider.notifier).addTask(
                        Task(title: title, description: description),
                      );
                  Navigator.pop(context);
                }
              },
              child: const Text('Add Task'),
            ),
          ],
        ),
      ),
    );
  }
}

(7)lib/screens/edit_task_screen.dart

タスク編集画面を作成します。

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

import '../models/task.dart';
import '../providers/task_provider.dart';

class EditTaskScreen extends ConsumerWidget {
  final int index;
  final Task task;

  EditTaskScreen({super.key, required this.index, required this.task});

  final _titleController = TextEditingController();
  final _descriptionController = TextEditingController();

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    _titleController.text = task.title;
    _descriptionController.text = task.description;

    return Scaffold(
      appBar: AppBar(
        title: const Text('Edit Task'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextField(
              controller: _titleController,
              decoration: const InputDecoration(labelText: 'Title'),
            ),
            TextField(
              controller: _descriptionController,
              decoration: const InputDecoration(labelText: 'Description'),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                final title = _titleController.text;
                final description = _descriptionController.text;
                if (title.isNotEmpty && description.isNotEmpty) {
                  ref.read(taskListProvider.notifier).editTask(
                        index,
                        Task(title: title, description: description),
                      );
                  Navigator.pop(context);
                }
              },
              child: const Text('Save Changes'),
            ),
          ],
        ),
      ),
    );
  }
}

(8)lib/widgets/task_list.dart

タスクリストを表示するウィジェットを作成します。

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

import '../providers/task_provider.dart';
import '../screens/edit_task_screen.dart';

class TaskList extends ConsumerWidget {
  const TaskList({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final taskList = ref.watch(taskListProvider);

    return ListView.builder(
      itemCount: taskList.length,
      itemBuilder: (context, index) {
        final task = taskList[index];
        return ListTile(
          title: Text(task.title),
          subtitle: Text(task.description),
          trailing: Checkbox(
            value: task.isCompleted,
            onChanged: (value) {
              ref.read(taskListProvider.notifier).toggleTaskCompletion(index);
            },
          ),
          onTap: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => EditTaskScreen(index: index, task: task),
              ),
            );
          },
        );
      },
    );
  }
}

コメントを残す