Blocを使ったタスク管理アプリケーションのサンプルコード
1.ファイル構成と役割
pubspec.yaml
- 役割: アプリケーションで使用するパッケージを管理するファイル。
- 自動生成:
flutter create <project_name>
コマンドを実行すると生成されます。
lib/main.dart
- 役割: アプリケーションのエントリーポイント。Blocの設定を行います。
lib/models/task.dart
- 役割: タスクモデルを定義するファイル。
lib/bloc/task_bloc.dart
- 役割: タスクのビジネスロジックを管理するBlocファイル。
lib/bloc/task_event.dart
- 役割: Blocで使用するイベントを定義するファイル。
lib/bloc/task_state.dart
- 役割: Blocで使用する状態を定義するファイル。
lib/screens/home_screen.dart
- 役割: タスクのリストと追加ボタンを表示するホーム画面。
lib/screens/add_task_screen.dart
- 役割: 新しいタスクを追加する画面。
lib/screens/edit_task_screen.dart
- 役割: 既存のタスクを編集する画面。
lib/widgets/task_list.dart
- 役割: タスクリストを表示するウィジェット。
2.サンプルコード
(1)pubspec.yaml
必要なパッケージ(flutter_bloc、equatable)を追加します。
Dart
name: task_management_app
description: A new Flutter project using Bloc for state management.
publish_to: 'none'
version: 1.0.0+1
environment:
sdk: ">=2.12.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
flutter_bloc: ^7.0.0
equatable: ^2.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_bloc/flutter_bloc.dart';
import 'bloc/task_bloc.dart';
import 'screens/home_screen.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Task Management App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: BlocProvider(
create: (context) => TaskBloc(),
child: 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/bloc/task_event.dart
Blocで使用するイベントを定義します。
Dart
import 'package:equatable/equatable.dart';
import '../models/task.dart';
// タスクイベントの基底クラス
abstract class TaskEvent extends Equatable {
const TaskEvent();
@override
List<Object> get props => [];
}
// タスク追加イベント
class AddTask extends TaskEvent {
final Task task;
const AddTask(this.task);
@override
List<Object> get props => [task];
}
// タスク編集イベント
class EditTask extends TaskEvent {
final int index;
final Task task;
const EditTask(this.index, this.task);
@override
List<Object> get props => [index, task];
}
// タスク削除イベント
class RemoveTask extends TaskEvent {
final int index;
const RemoveTask(this.index);
@override
List<Object> get props => [index];
}
// タスク完了状態トグルイベント
class ToggleTaskCompletion extends TaskEvent {
final int index;
const ToggleTaskCompletion(this.index);
@override
List<Object> get props => [index];
}
(5)lib/bloc/task_state.dart
Blocで使用する状態を定義します。
Dart
import 'package:equatable/equatable.dart';
import '../models/task.dart';
// タスク状態の基底クラス
abstract class TaskState extends Equatable {
const TaskState();
@override
List<Object> get props => [];
}
// 初期状態
class TaskInitial extends TaskState {}
// タスクリスト状態
class TaskLoaded extends TaskState {
final List<Task> tasks;
const TaskLoaded(this.tasks);
@override
List<Object> get props => [tasks];
}
(6)lib/bloc/task_bloc.dart
タスクのビジネスロジックを管理するBlocファイルを作成します。
Dart
import 'package:flutter_bloc/flutter_bloc.dart';
import '../models/task.dart';
import 'task_event.dart';
import 'task_state.dart';
// タスクBlocクラス
class TaskBloc extends Bloc<TaskEvent, TaskState> {
TaskBloc() : super(TaskInitial()) {
on<AddTask>((event, emit) {
if (state is TaskLoaded) {
final updatedTasks = List<Task>.from((state as TaskLoaded).tasks)
..add(event.task);
emit(TaskLoaded(updatedTasks));
}
});
on<EditTask>((event, emit) {
if (state is TaskLoaded) {
final updatedTasks = List<Task>.from((state as TaskLoaded).tasks);
updatedTasks[event.index] = event.task;
emit(TaskLoaded(updatedTasks));
}
});
on<RemoveTask>((event, emit) {
if (state is TaskLoaded) {
final updatedTasks = List<Task>.from((state as TaskLoaded).tasks)
..removeAt(event.index);
emit(TaskLoaded(updatedTasks));
}
});
on<ToggleTaskCompletion>((event, emit) {
if (state is TaskLoaded) {
final updatedTasks = List<Task>.from((state as TaskLoaded).tasks);
final task = updatedTasks[event.index];
updatedTasks[event.index] = Task(
title: task.title,
description: task.description,
isCompleted: !task.isCompleted,
);
emit(TaskLoaded(updatedTasks));
}
});
}
}
(7)lib/screens/home_screen.dart
ホーム画面を作成します。
Dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../bloc/task_bloc.dart';
import '../bloc/task_state.dart';
import '../widgets/task_list.dart';
import 'add_task_screen.dart';
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Task Management'),
),
body: BlocBuilder<TaskBloc, TaskState>(
builder: (context, state) {
if (state is TaskInitial) {
context.read<TaskBloc>().add(TaskEvent());
return Center(child: CircularProgressIndicator());
} else if (state is TaskLoaded) {
return TaskList(tasks: state.tasks);
}
return Container();
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => AddTaskScreen()),
);
},
child: Icon(Icons.add),
),
);
}
}
(8)lib/screens/add_task_screen.dart
タスク追加画面を作成します。
Dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../bloc/task_bloc.dart';
import '../bloc/task_event.dart';
import '../models/task.dart';
class AddTaskScreen extends StatelessWidget {
final _titleController = TextEditingController();
final _descriptionController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add Task'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: _titleController,
decoration: InputDecoration(labelText: 'Title'),
),
TextField(
controller: _descriptionController,
decoration: InputDecoration(labelText: 'Description'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
final title = _titleController.text;
final description = _descriptionController.text;
if (title.isNotEmpty && description.isNotEmpty) {
BlocProvider.of<TaskBloc>(context).add(
AddTask(Task(title: title, description: description)),
);
Navigator.pop(context);
}
},
child: Text('Add Task'),
),
],
),
),
);
}
}
(9)lib/screens/edit_task_screen.dar
タスク編集画面を作成します。
Dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../bloc/task_bloc.dart';
import '../bloc/task_event.dart';
import '../models/task.dart';
class EditTaskScreen extends StatelessWidget {
final int index;
final Task task;
EditTaskScreen({required this.index, required this.task});
final _titleController = TextEditingController();
final _descriptionController = TextEditingController();
@override
Widget build(BuildContext context) {
_titleController.text = task.title;
_descriptionController.text = task.description;
return Scaffold(
appBar: AppBar(
title: Text('Edit Task'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: _titleController,
decoration: InputDecoration(labelText: 'Title'),
),
TextField(
controller: _descriptionController,
decoration: InputDecoration(labelText: 'Description'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
final title = _titleController.text;
final description = _descriptionController.text;
if (title.isNotEmpty && description.isNotEmpty) {
BlocProvider.of<TaskBloc>(context).add(
EditTask(index, Task(title: title, description: description)),
);
Navigator.pop(context);
}
},
child: Text('Save Changes'),
),
],
),
),
);
}
}
(10)lib/widgets/task_list.dart
タスクリストを表示するウィジェットを作成します。
Dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../bloc/task_bloc.dart';
import '../bloc/task_event.dart';
import '../models/task.dart';
import '../screens/edit_task_screen.dart';
class TaskList extends StatelessWidget {
final List<Task> tasks;
TaskList({required this.tasks});
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: tasks.length,
itemBuilder: (context, index) {
final task = tasks[index];
return ListTile(
title: Text(task.title),
subtitle: Text(task.description),
trailing: Checkbox(
value: task.isCompleted,
onChanged: (value) {
context.read<TaskBloc>().add(ToggleTaskCompletion(index));
},
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EditTaskScreen(index: index, task: task),
),
);
},
);
},
);
}
}