Skip to content

Commit

Permalink
Merge pull request #2 from dkaera/feature/enum_support
Browse files Browse the repository at this point in the history
Feature - enum support
  • Loading branch information
dkaera authored Aug 4, 2022
2 parents e55a251 + d204b0d commit 1c66273
Show file tree
Hide file tree
Showing 17 changed files with 506 additions and 171 deletions.
1 change: 1 addition & 0 deletions docs/entities.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Floor entities can hold values of the following Dart types which map to their co
- `String` - TEXT
- `bool` - INTEGER (0 = false, 1 = true)
- `Uint8List` - BLOB
- `enum` - INTEGER (records by the index 0..n)

In case you want to store sophisticated Dart objects that can be represented by one of the above types, take a look at [Type Converters](type-converters.md).

Expand Down
40 changes: 33 additions & 7 deletions example/lib/database.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

114 changes: 97 additions & 17 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class FloorApp extends StatelessWidget {
}
}

class TasksWidget extends StatelessWidget {
class TasksWidget extends StatefulWidget {
final String title;
final TaskDao dao;

Expand All @@ -49,35 +49,75 @@ class TasksWidget extends StatelessWidget {
required this.dao,
}) : super(key: key);

@override
State<StatefulWidget> createState() => TasksWidgetState();
}

class TasksWidgetState extends State<TasksWidget> {
TaskType? _selectedType;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(title)),
appBar: AppBar(
title: Text(widget.title),
actions: <Widget>[
PopupMenuButton<int>(
itemBuilder: (context) {
return List.generate(
TaskType.values.length + 1, //Uses increment to handle All types
(index) {
return PopupMenuItem<int>(
value: index,
child: Text(
index == 0 ? 'All' : _getMenuType(index).title,
),
);
},
);
},
onSelected: (index) {
setState(() {
_selectedType = index == 0 ? null : _getMenuType(index);
});
},
)
],
),
body: SafeArea(
child: Column(
children: <Widget>[
TasksListView(dao: dao),
TasksTextField(dao: dao),
TasksListView(
dao: widget.dao,
selectedType: _selectedType,
),
TasksTextField(dao: widget.dao),
],
),
),
);
}

TaskType _getMenuType(int index) => TaskType.values[index - 1];
}

class TasksListView extends StatelessWidget {
final TaskDao dao;
final TaskType? selectedType;

const TasksListView({
Key? key,
required this.dao,
required this.selectedType,
}) : super(key: key);

@override
Widget build(BuildContext context) {
return Expanded(
child: StreamBuilder<List<Task>>(
stream: dao.findAllTasksAsStream(),
stream: selectedType == null
? dao.findAllTasksAsStream()
: dao.findAllTasksByTypeAsStream(selectedType!),
builder: (_, snapshot) {
if (!snapshot.hasData) return Container();

Expand Down Expand Up @@ -112,20 +152,60 @@ class TaskListCell extends StatelessWidget {
Widget build(BuildContext context) {
return Dismissible(
key: Key('${task.hashCode}'),
background: Container(color: Colors.red),
direction: DismissDirection.endToStart,
background: Container(
padding: const EdgeInsets.only(left: 16),
color: Colors.green,
child: const Align(
child: Text(
'Change status',
style: TextStyle(color: Colors.white),
),
alignment: Alignment.centerLeft,
),
),
secondaryBackground: Container(
padding: const EdgeInsets.only(right: 16),
color: Colors.red,
child: const Align(
child: Text(
'Delete',
style: TextStyle(color: Colors.white),
),
alignment: Alignment.centerRight,
),
),
direction: DismissDirection.horizontal,
child: ListTile(
leading: Text(task.message),
title: Text(task.message),
subtitle: Text('Status: ${task.type.title}'),
trailing: Text(task.timestamp.toIso8601String()),
),
onDismissed: (_) async {
await dao.deleteTask(task);

final scaffoldMessengerState = ScaffoldMessenger.of(context);
scaffoldMessengerState.hideCurrentSnackBar();
scaffoldMessengerState.showSnackBar(
const SnackBar(content: Text('Removed task')),
);
confirmDismiss: (direction) async {
String? statusMessage;
switch (direction) {
case DismissDirection.endToStart:
await dao.deleteTask(task);
statusMessage = 'Removed task';
break;
case DismissDirection.startToEnd:
final tasksLength = TaskType.values.length;
final nextIndex = (tasksLength + task.type.index + 1) % tasksLength;
final taskCopy = task.copyWith(type: TaskType.values[nextIndex]);
await dao.updateTask(taskCopy);
statusMessage = 'Updated task status by: ${taskCopy.type.title}';
break;
default:
break;
}

if (statusMessage != null) {
final scaffoldMessengerState = ScaffoldMessenger.of(context);
scaffoldMessengerState.hideCurrentSnackBar();
scaffoldMessengerState.showSnackBar(
SnackBar(content: Text(statusMessage)),
);
}
return statusMessage != null;
},
);
}
Expand Down Expand Up @@ -181,7 +261,7 @@ class TasksTextField extends StatelessWidget {
if (message.trim().isEmpty) {
_textEditingController.clear();
} else {
final task = Task(null, message, DateTime.now());
final task = Task.optional(message: message);
await dao.insertTask(task);
_textEditingController.clear();
}
Expand Down
68 changes: 60 additions & 8 deletions example/lib/task.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,65 @@
import 'package:floor/floor.dart';

enum TaskType {
open('Open'),
inProgress('In Progress'),
done('Done');

final String title;

const TaskType(this.title);
}

@entity
class Task {
@PrimaryKey(autoGenerate: true)
final int? id;

final String message;

final bool isRead;

final DateTime timestamp;

Task(this.id, this.message, this.timestamp);
final TaskType type;

Task(this.id, this.isRead, this.message, this.timestamp, this.type);

factory Task.optional({
int? id,
DateTime? timestamp,
String? message,
bool? isRead,
TaskType? type,
}) =>
Task(
id,
isRead ?? false,
message ?? 'empty',
timestamp ?? DateTime.now(),
type ?? TaskType.open,
);

@override
String toString() {
return 'Task{id: $id, message: $message, read: $isRead, timestamp: $timestamp, type: $type}';
}

Task copyWith({
int? id,
String? message,
bool? isRead,
DateTime? timestamp,
TaskType? type,
}) {
return Task(
id ?? this.id,
isRead ?? this.isRead,
message ?? this.message,
timestamp ?? this.timestamp,
type ?? this.type,
);
}

@override
bool operator ==(Object other) =>
Expand All @@ -18,15 +68,17 @@ class Task {
runtimeType == other.runtimeType &&
id == other.id &&
message == other.message &&
timestamp == other.timestamp;
isRead == other.isRead &&
timestamp == other.timestamp &&
type == other.type;

@override
int get hashCode => id.hashCode ^ message.hashCode ^ timestamp.hashCode;

@override
String toString() {
return 'Task{id: $id, message: $message, timestamp: $timestamp}';
}
int get hashCode =>
id.hashCode ^
message.hashCode ^
isRead.hashCode ^
timestamp.hashCode ^
type.hashCode;
}

@DatabaseView('SELECT message as message FROM task', viewName: 'messages')
Expand Down
3 changes: 3 additions & 0 deletions example/lib/task_dao.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ abstract class TaskDao {
@Query('SELECT * FROM task')
Stream<List<Task>> findAllTasksAsStream();

@Query('SELECT * FROM task WHERE type = :type')
Stream<List<Task>> findAllTasksByTypeAsStream(TaskType type);

@insert
Future<void> insertTask(Task task);

Expand Down
Loading

0 comments on commit 1c66273

Please sign in to comment.